summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Doffman <mark.doffman@codethink.co.uk>2014-04-01 20:02:03 +0000
committerMark Doffman <mark.doffman@codethink.co.uk>2014-04-01 20:02:03 +0000
commitce764489e358bad6b49418f5c8bc7b25a4b1815e (patch)
treebdf7d009702adc91e63d261f7d924d9e5bf94cff
parent0d45677b0a20270735e4d18e969a4991a4e67078 (diff)
parent6c14af04775a9de4dfa3fa0cc15a7ad0462ef3d9 (diff)
downloadlibsoup-ce764489e358bad6b49418f5c8bc7b25a4b1815e.tar.gz
Merge tag '2.46.0' into baserock/morphbaserock/morph
2.46.0
-rw-r--r--.gitignore31
-rw-r--r--Makefile.am6
-rw-r--r--Makefile.glib22
-rw-r--r--NEWS774
-rwxr-xr-xautogen.sh42
-rw-r--r--configure.ac122
-rw-r--r--data/effective_tld_names.dat2848
-rw-r--r--docs/reference/Makefile.am24
-rw-r--r--docs/reference/build-howto.xml73
-rw-r--r--docs/reference/client-howto.xml413
-rw-r--r--docs/reference/libsoup-2.4-docs.sgml28
-rw-r--r--docs/reference/libsoup-2.4-sections.txt250
-rw-r--r--docs/reference/request-howto.xml184
-rw-r--r--docs/reference/server-howto.xml34
-rw-r--r--docs/reference/session-porting.xml221
-rw-r--r--examples/Makefile.am14
-rw-r--r--examples/get.c195
-rw-r--r--examples/simple-httpd.c (renamed from tests/simple-httpd.c)125
-rw-r--r--examples/simple-proxy.c (renamed from tests/simple-proxy.c)69
-rw-r--r--glib-tap.mk134
-rw-r--r--libsoup.doap1
-rw-r--r--libsoup/Makefile.am73
-rw-r--r--libsoup/libsoup-2.4.sym508
-rw-r--r--libsoup/libsoup-gnome-2.4.sym5
-rw-r--r--libsoup/soup-address.c230
-rw-r--r--libsoup/soup-address.h8
-rw-r--r--libsoup/soup-auth-basic.c13
-rw-r--r--libsoup/soup-auth-digest.c18
-rw-r--r--libsoup/soup-auth-domain-basic.c1
-rw-r--r--libsoup/soup-auth-domain-digest.c3
-rw-r--r--libsoup/soup-auth-manager-ntlm.c1360
-rw-r--r--libsoup/soup-auth-manager-ntlm.h36
-rw-r--r--libsoup/soup-auth-manager.c343
-rw-r--r--libsoup/soup-auth-manager.h13
-rw-r--r--libsoup/soup-auth-ntlm.c1201
-rw-r--r--libsoup/soup-auth-ntlm.h11
-rw-r--r--libsoup/soup-auth.c224
-rw-r--r--libsoup/soup-auth.h42
-rw-r--r--libsoup/soup-body-input-stream.c141
-rw-r--r--libsoup/soup-body-input-stream.h6
-rw-r--r--libsoup/soup-body-output-stream.c50
-rw-r--r--libsoup/soup-cache-input-stream.c351
-rw-r--r--libsoup/soup-cache-input-stream.h46
-rw-r--r--libsoup/soup-cache-private.h20
-rw-r--r--libsoup/soup-cache.c811
-rw-r--r--libsoup/soup-cache.h12
-rw-r--r--libsoup/soup-client-input-stream.c125
-rw-r--r--libsoup/soup-connection-auth.c173
-rw-r--r--libsoup/soup-connection-auth.h52
-rw-r--r--libsoup/soup-connection.c633
-rw-r--r--libsoup/soup-connection.h38
-rw-r--r--libsoup/soup-content-decoder.c207
-rw-r--r--libsoup/soup-content-decoder.h3
-rw-r--r--libsoup/soup-content-processor.c51
-rw-r--r--libsoup/soup-content-processor.h56
-rw-r--r--libsoup/soup-content-sniffer-stream.c23
-rw-r--r--libsoup/soup-content-sniffer-stream.h4
-rw-r--r--libsoup/soup-content-sniffer.c697
-rw-r--r--libsoup/soup-content-sniffer.h4
-rw-r--r--libsoup/soup-converter-wrapper.c28
-rw-r--r--libsoup/soup-cookie-jar-db.c337
-rw-r--r--libsoup/soup-cookie-jar-db.h46
-rw-r--r--libsoup/soup-cookie-jar-sqlite.c298
-rw-r--r--libsoup/soup-cookie-jar-sqlite.h15
-rw-r--r--libsoup/soup-cookie-jar-text.h2
-rw-r--r--libsoup/soup-cookie-jar.c16
-rw-r--r--libsoup/soup-cookie-jar.h22
-rwxr-xr-xlibsoup/soup-cookie.c28
-rw-r--r--libsoup/soup-cookie.h31
-rw-r--r--libsoup/soup-date.c18
-rw-r--r--libsoup/soup-date.h19
-rw-r--r--libsoup/soup-filter-input-stream.c53
-rw-r--r--libsoup/soup-filter-input-stream.h1
-rw-r--r--libsoup/soup-form.c3
-rw-r--r--libsoup/soup-form.h4
-rw-r--r--libsoup/soup-gnome-features.c35
-rw-r--r--libsoup/soup-gnome-features.h10
-rw-r--r--libsoup/soup-headers.c5
-rw-r--r--libsoup/soup-headers.h4
-rw-r--r--libsoup/soup-io-stream.c2
-rw-r--r--libsoup/soup-logger.c64
-rw-r--r--libsoup/soup-logger.h8
-rw-r--r--libsoup/soup-message-body.c12
-rw-r--r--libsoup/soup-message-body.h11
-rw-r--r--libsoup/soup-message-client-io.c101
-rw-r--r--libsoup/soup-message-headers.c142
-rw-r--r--libsoup/soup-message-headers.h21
-rw-r--r--libsoup/soup-message-io.c223
-rw-r--r--libsoup/soup-message-private.h30
-rw-r--r--libsoup/soup-message-queue.c96
-rw-r--r--libsoup/soup-message-queue.h17
-rw-r--r--libsoup/soup-message-server-io.c30
-rw-r--r--libsoup/soup-message.c537
-rw-r--r--libsoup/soup-message.h65
-rw-r--r--libsoup/soup-misc-private.h54
-rw-r--r--libsoup/soup-misc.c21
-rw-r--r--libsoup/soup-misc.h3
-rw-r--r--libsoup/soup-multipart-input-stream.c592
-rw-r--r--libsoup/soup-multipart-input-stream.h66
-rw-r--r--libsoup/soup-multipart.h9
-rw-r--r--libsoup/soup-password-manager-gnome.c198
-rw-r--r--libsoup/soup-password-manager.c47
-rw-r--r--libsoup/soup-password-manager.h30
-rw-r--r--libsoup/soup-proxy-resolver-default.c19
-rw-r--r--libsoup/soup-proxy-resolver-gnome.c3
-rw-r--r--libsoup/soup-proxy-resolver-static.c177
-rw-r--r--libsoup/soup-proxy-resolver-static.h35
-rw-r--r--libsoup/soup-proxy-resolver-wrapper.c167
-rw-r--r--libsoup/soup-proxy-resolver-wrapper.h34
-rw-r--r--libsoup/soup-proxy-resolver.c132
-rw-r--r--libsoup/soup-proxy-resolver.h5
-rw-r--r--libsoup/soup-proxy-uri-resolver.c43
-rw-r--r--libsoup/soup-proxy-uri-resolver.h6
-rw-r--r--libsoup/soup-request-data.c19
-rw-r--r--libsoup/soup-request-data.h5
-rw-r--r--libsoup/soup-request-file.c64
-rw-r--r--libsoup/soup-request-file.h6
-rw-r--r--libsoup/soup-request-http.c191
-rw-r--r--libsoup/soup-request-http.h10
-rw-r--r--libsoup/soup-request.c96
-rw-r--r--libsoup/soup-request.h13
-rw-r--r--libsoup/soup-requester.c191
-rw-r--r--libsoup/soup-requester.h11
-rw-r--r--libsoup/soup-server.c167
-rw-r--r--libsoup/soup-server.h2
-rw-r--r--libsoup/soup-session-async.c658
-rw-r--r--libsoup/soup-session-async.h2
-rw-r--r--libsoup/soup-session-feature.c8
-rw-r--r--libsoup/soup-session-feature.h6
-rw-r--r--libsoup/soup-session-private.h24
-rw-r--r--libsoup/soup-session-sync.c466
-rw-r--r--libsoup/soup-session-sync.h2
-rw-r--r--libsoup/soup-session.c2607
-rw-r--r--libsoup/soup-session.h74
-rw-r--r--libsoup/soup-socket.c482
-rw-r--r--libsoup/soup-socket.h8
-rw-r--r--libsoup/soup-status.c15
-rw-r--r--libsoup/soup-status.h83
-rw-r--r--libsoup/soup-tld.c126
-rw-r--r--libsoup/soup-tld.h5
-rw-r--r--libsoup/soup-types.h3
-rw-r--r--libsoup/soup-uri.c210
-rw-r--r--libsoup/soup-uri.h24
-rw-r--r--libsoup/soup-value-utils.c2
-rw-r--r--libsoup/soup-version.c307
-rw-r--r--libsoup/soup-version.h.in261
-rw-r--r--libsoup/soup-xmlrpc.c38
-rw-r--r--libsoup/soup.h10
-rwxr-xr-xlibsoup/tld-parser.py5
-rw-r--r--m4/glibtests.m428
-rw-r--r--po/LINGUAS38
-rw-r--r--po/POTFILES.in5
-rw-r--r--po/an.po108
-rw-r--r--po/as.po77
-rw-r--r--po/be.po69
-rw-r--r--po/bg.po67
-rw-r--r--po/bn_IN.po83
-rw-r--r--po/ca.po108
-rw-r--r--po/ca@valencia.po107
-rw-r--r--po/cs.po110
-rw-r--r--po/da.po108
-rw-r--r--po/de.po110
-rw-r--r--po/el.po87
-rw-r--r--po/en_GB.po84
-rw-r--r--po/eo.po74
-rw-r--r--po/es.po74
-rw-r--r--po/et.po85
-rw-r--r--po/eu.po106
-rw-r--r--po/fa.po109
-rw-r--r--po/fr.po82
-rw-r--r--po/fur.po81
-rw-r--r--po/gl.po77
-rw-r--r--po/gu.po111
-rw-r--r--po/he.po72
-rw-r--r--po/hi.po113
-rw-r--r--po/hu.po112
-rw-r--r--po/id.po75
-rw-r--r--po/it.po106
-rw-r--r--po/ja.po81
-rw-r--r--po/kn.po109
-rw-r--r--po/ko.po109
-rw-r--r--po/lt.po84
-rw-r--r--po/lv.po113
-rw-r--r--po/ml.po109
-rw-r--r--po/mr.po111
-rw-r--r--po/nb.po71
-rw-r--r--po/nl.po109
-rw-r--r--po/or.po110
-rw-r--r--po/pa.po81
-rw-r--r--po/pl.po75
-rw-r--r--po/pt.po105
-rw-r--r--po/pt_BR.po108
-rw-r--r--po/ru.po93
-rw-r--r--po/sk.po114
-rw-r--r--po/sl.po82
-rw-r--r--po/sr.po80
-rw-r--r--po/sr@latin.po80
-rw-r--r--po/sv.po75
-rw-r--r--po/ta.po112
-rw-r--r--po/te.po80
-rw-r--r--po/tg.po107
-rw-r--r--po/th.po107
-rw-r--r--po/tr.po110
-rw-r--r--po/ug.po105
-rw-r--r--po/uk.po84
-rw-r--r--po/uz@cyrillic.po82
-rw-r--r--po/vi.po70
-rw-r--r--po/zh_CN.po82
-rw-r--r--po/zh_HK.po68
-rw-r--r--po/zh_TW.po68
-rwxr-xr-xtap-driver.sh652
-rwxr-xr-xtap-test5
-rw-r--r--tests/Makefile.am241
-rw-r--r--tests/auth-test.c474
-rw-r--r--tests/cache-test.c659
-rw-r--r--tests/chunk-io-test.c610
-rw-r--r--tests/chunk-test.c190
-rw-r--r--tests/coding-test.c689
-rw-r--r--tests/connection-test.c509
-rw-r--r--tests/context-test.c90
-rw-r--r--tests/continue-test.c107
-rw-r--r--tests/cookies-test.c73
-rw-r--r--tests/date.c376
-rw-r--r--tests/dns.c62
-rw-r--r--tests/forms-test.c164
-rw-r--r--tests/get.c189
-rw-r--r--tests/header-parsing.c472
-rw-r--r--tests/httpd.conf.22.in (renamed from tests/httpd.conf.in)81
-rw-r--r--tests/httpd.conf.24.in276
-rw-r--r--tests/libsoup.supp74
-rw-r--r--tests/misc-test.c588
-rw-r--r--tests/multipart-test.c521
-rw-r--r--tests/no-ssl-test.c151
-rw-r--r--tests/ntlm-test-helper.c103
-rw-r--r--tests/ntlm-test.c281
-rw-r--r--tests/proxy-test.c231
-rw-r--r--tests/pull-api.c180
-rw-r--r--tests/range-test.c216
-rw-r--r--tests/redirect-test.c416
-rw-r--r--tests/requester-test.c594
-rw-r--r--tests/resource-test.c220
-rw-r--r--tests/resources/feed.rdf32
-rw-r--r--tests/resources/home.jpgbin0 -> 1074 bytes
-rw-r--r--tests/resources/home.pngbin0 -> 313 bytes
-rw-r--r--tests/resources/html_binary.html2
-rw-r--r--tests/resources/leading_space.html12
-rw-r--r--tests/resources/test.aiffbin0 -> 384088 bytes
-rw-r--r--tests/resources/test.mp4bin0 -> 192844 bytes
-rw-r--r--tests/resources/test.oggbin0 -> 16994 bytes
-rw-r--r--tests/resources/test.wavbin0 -> 384080 bytes
-rw-r--r--tests/resources/test.webmbin0 -> 149879 bytes
-rw-r--r--tests/resources/text.txt1
-rw-r--r--tests/resources/tux.webpbin0 -> 17128 bytes
-rw-r--r--tests/server-auth-test.c274
-rw-r--r--tests/server-test.c329
-rw-r--r--tests/session-test.c396
-rw-r--r--tests/sniffing-test.c541
-rw-r--r--tests/socket-test.c124
-rw-r--r--tests/soup-tests.gresource.xml28
-rw-r--r--tests/ssl-test.c321
-rw-r--r--tests/streaming-test.c99
-rw-r--r--tests/test-utils.c405
-rw-r--r--tests/test-utils.h108
-rw-r--r--tests/timeout-test.c300
-rw-r--r--tests/tld-test.c278
-rw-r--r--tests/uri-parsing.c515
-rw-r--r--tests/xmlrpc-server-test.c167
-rw-r--r--tests/xmlrpc-server.php17
-rw-r--r--tests/xmlrpc-test.c305
269 files changed, 29849 insertions, 12717 deletions
diff --git a/.gitignore b/.gitignore
index c3873607..674fad1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,6 @@
-*~
*.o
*.lo
*.la
-*.stamp
-*.bak
.deps
.libs
INSTALL
@@ -11,16 +8,15 @@ Makefile
Makefile.in
aclocal.m4
autom4te.cache
-config.guess
+build-aux
config.h
config.h.in
+config.h.in~
config.log
config.status
-config.sub
configure
-depcomp
-docs/reference/html-build.stamp
-docs/reference/html.stamp
+docs/reference/*.stamp
+docs/reference/*.bak
docs/reference/html
docs/reference/libsoup-2.4-decl-list.txt
docs/reference/libsoup-2.4-decl.txt
@@ -36,40 +32,39 @@ docs/reference/libsoup-2.4.signals
docs/reference/libsoup-2.4.types
docs/reference/tmpl
docs/reference/xml
+examples/get
+examples/simple-httpd
+examples/simple-proxy
gtk-doc.make
-install-sh
libsoup-zip
libsoup-2.4.pc
libsoup-gnome-2.4.pc
libsoup/soup-enum-types.*
-libsoup/soup-marshal.*
libsoup/Soup-2.4.gir
libsoup/Soup-2.4.typelib
libsoup/SoupGNOME-2.4.gir
libsoup/SoupGNOME-2.4.typelib
libsoup/tld_data.inc
+libsoup/soup-version.h
libtool
-ltmain.sh
m4/gtk-doc.m4
m4/intltool.m4
m4/libtool.m4
m4/lt*.m4
-missing
-mkinstalldirs
po/*.gmo
po/libsoup.pot
po/Makefile.in.in
po/POTFILES
po/stamp-it
stamp-h1
+tests/*.log
+tests/*.trs
tests/*-test
+tests/*.test
tests/date
-tests/dns
-tests/get
-tests/getbug
tests/header-parsing
tests/httpd.conf
+tests/ntlm-test-helper
tests/pull-api
-tests/simple-httpd
-tests/simple-proxy
+tests/soup-tests.gresource
tests/uri-parsing
diff --git a/Makefile.am b/Makefile.am
index 18bf80af..c8fa95ce 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
## Process this file with automake to produce Makefile.in
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
-SUBDIRS = libsoup po tests docs
+SUBDIRS = libsoup po tests examples docs
EXTRA_DIST = \
data/effective_tld_names.dat \
@@ -9,7 +9,9 @@ EXTRA_DIST = \
libsoup-gnome-2.4.pc.in \
gtk-doc.make \
libsoup-zip.in \
- m4/introspection.m4 \
+ glib-tap.mk \
+ tap-driver.sh \
+ tap-test \
Makefile.glib
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-introspection
diff --git a/Makefile.glib b/Makefile.glib
index 78eb07d7..c28c983b 100644
--- a/Makefile.glib
+++ b/Makefile.glib
@@ -128,7 +128,7 @@ _GLIB_DISTCLEANFILES =
_GLIB_V_GEN = $(_glib_v_gen_$(V))
_glib_v_gen_ = $(_glib_v_gen_$(AM_DEFAULT_VERBOSITY))
-_glib_v_gen_0 = @echo " GEN " $(subst .stamp,,$@);
+_glib_v_gen_0 = @echo " GEN " $(subst .stamp,,$@);
### glib-genmarshal
@@ -154,8 +154,8 @@ _glib_marshal_sources = $(filter-out %.h,$(filter-out $(GLIB_GENERATED),$($(_gli
define _glib_make_genmarshal_rules
$(if $(_glib_marshal_sources),,$(error Need to define $(_glib_marshal_sources_var) for $(1).[ch]))
-$(1).list.stamp: $(_glib_marshal_sources)
- $$(_GLIB_V_GEN) LC_ALL=C sed -ne 's/.*_$(_glib_marshal_prefix)_\([_A-Z]*\).*/\1/p' $$^ | sort -u | sed -e 's/__/:/' -e 's/_/,/g' > $(1).list.tmp && \
+$(1).list.stamp: $(_glib_marshal_sources) Makefile
+ $$(_GLIB_V_GEN) LC_ALL=C sed -ne 's/.*_$(_glib_marshal_prefix)_\([_A-Z]*\).*/\1/p' $$(filter-out Makefile, $$^) | sort -u | sed -e 's/__/:/' -e 's/_/,/g' > $(1).list.tmp && \
(cmp -s $(1).list.tmp $(1).list || cp $(1).list.tmp $(1).list) && \
rm -f $(1).list.tmp && \
echo timestamp > $$@
@@ -200,7 +200,7 @@ _glib_enum_types_h_sources = $(filter %.h,$(_glib_enum_types_sources))
define _glib_make_mkenums_rules
$(if $(_glib_enum_types_sources),,$(error Need to define $(_glib_enum_types_sources_var) for $(1).[ch]))
-$(1).h.stamp: $(_glib_enum_types_h_sources)
+$(1).h.stamp: $(_glib_enum_types_h_sources) Makefile
$$(_GLIB_V_GEN) $$(GLIB_MKENUMS) \
--fhead "/* Generated by glib-mkenums. Do not edit */\n\n#ifndef $(_glib_enum_types_guard)\n#define $(_glib_enum_types_guard)\n\n" \
$$(GLIB_MKENUMS_H_FLAGS) \
@@ -208,7 +208,7 @@ $(1).h.stamp: $(_glib_enum_types_h_sources)
--fhead "#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
--vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())\n" \
--ftail "G_END_DECLS\n\n#endif /* $(_glib_enum_types_guard) */" \
- $$^ > $(1).h.tmp && \
+ $$(filter-out Makefile, $$^) > $(1).h.tmp && \
(cmp -s $(1).h.tmp $(1).h || cp $(1).h.tmp $(1).h) && \
rm -f $(1).h.tmp && \
echo timestamp > $$@
@@ -216,16 +216,16 @@ $(1).h.stamp: $(_glib_enum_types_h_sources)
$(1).h: $(1).h.stamp
@true
-$(1).c.stamp: $(_glib_enum_types_h_sources)
+$(1).c.stamp: $(_glib_enum_types_h_sources) Makefile
$$(_GLIB_V_GEN) $$(GLIB_MKENUMS) \
--fhead "/* Generated by glib-mkenums. Do not edit */\n\n#include \"$(notdir $(1)).h\"\n" \
$$(GLIB_MKENUMS_C_FLAGS) \
$$($(_glib_enum_types_prefix)_MKENUMS_C_FLAGS) \
- --fhead "$$(foreach f,$$(^F),\n#include \"$$(f)\")\n\n" \
+ --fhead "$$(foreach f,$$(filter-out Makefile,$$(^F)),\n#include \"$$(f)\")\n\n" \
--vhead "GType\n@enum_name@_get_type (void)\n{\n static volatile gsize g_define_type_id__volatile = 0;\n\n if (g_once_init_enter (&g_define_type_id__volatile))\n {\n static const G@Type@Value values[] = {\n" \
--vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" },\n" \
--vtail " { 0, NULL, NULL }\n };\n GType g_define_type_id =\n g_@type@_register_static (g_intern_static_string (\"@EnumName@\"), values);\n g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);\n }\n\n return g_define_type_id__volatile;\n}\n" \
- $$^ > $(1).c.tmp && \
+ $$(filter-out Makefile, $$^) > $(1).c.tmp && \
(cmp -s $(1).c.tmp $(1).c || cp $(1).c.tmp $(1).c) && \
rm -f $(1).c.tmp && \
echo timestamp > $$@
@@ -254,13 +254,13 @@ _glib_enums_xml_namespace = $(subst .enums.xml,,$(notdir $(1)))
define _glib_make_enums_xml_rule
$(if $(_glib_enums_xml_sources),,$(error Need to define $(_glib_enums_xml_sources_var) for $(1)))
-$(1): $(_glib_enums_xml_sources)
- $$(_GLIB_V_GEN) $$(GLIB_MKENUMS) --comments '<!-- @comment@ -->' --fhead "<schemalist>" --vhead " <@type@ id='$(_glib_enums_xml_namespace).@EnumName@'>" --vprod " <value nick='@valuenick@' value='@valuenum@'/>" --vtail " </@type@>" --ftail "</schemalist>" $$^ > $$@.tmp && mv $$@.tmp $$@
+$(1): $(_glib_enums_xml_sources) Makefile
+ $$(_GLIB_V_GEN) $$(GLIB_MKENUMS) --comments '<!-- @comment@ -->' --fhead "<schemalist>" --vhead " <@type@ id='$(_glib_enums_xml_namespace).@EnumName@'>" --vprod " <value nick='@valuenick@' value='@valuenum@'/>" --vtail " </@type@>" --ftail "</schemalist>" $$(filter-out Makefile, $$^) > $$@.tmp && mv $$@.tmp $$@
endef
_GLIB_V_CHECK = $(_glib_v_check_$(V))
_glib_v_check_ = $(_glib_v_check_$(AM_DEFAULT_VERBOSITY))
-_glib_v_check_0 = @echo " CHECK " $(subst .valid,.xml,$@);
+_glib_v_check_0 = @echo " CHECK " $(subst .valid,.xml,$@);
define _glib_make_schema_validate_rule
$(subst .xml,.valid,$(1)): $(_GLIB_ENUMS_XML_GENERATED) $(1)
diff --git a/NEWS b/NEWS
index 79baf8ee..b29aa035 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,777 @@
+Changes in libsoup from 2.45.92 to 2.46.0:
+
+ * (No changes, just a version bump)
+
+Changes in libsoup from 2.45.90 to 2.45.92:
+
+ * Fixed problems with using an http proxy on port 80. [patch
+ from Slava Monich on libsoup-list]
+
+ * Plugged a small per-connection leak. [patch from Slava
+ Monich on libsoup-list]
+
+ * Belatedly bumped up the glib requirement to 2.38, which is
+ required for the TAP driver.
+
+ * Fixed up some leftover issues from the test porting to
+ gtestutils:
+
+ test-utils: change http_debug command-line flag
+ xmlrpc-test: use g_test_skip()
+ redirect-test: remove a flaky test
+ auth-test: remove some erroneous SKIP_IF_NO_APACHEs
+ tests: split up some test programs into more tests
+ tests: remove debug_printf()s that are redundant with test names
+ tests: use g_test_bug() to annotate test cases
+
+Changes in libsoup from 2.45.3 to 2.45.90:
+
+ * Fixed a problem where libsoup would use the "http" proxy
+ settings for "https" requests [#724316, Matt Barnes]
+
+ * Updated SoupContentSniffer to match the current version of
+ the MIME sniffing specification [#648849 / #715126, Gustavo
+ Noronha Silva]
+
+ * Updated the soup-tld APIs to accept ASCII-encoded hostnames
+ in addition to UTF-8 ones.
+
+ * Ported the test programs to the gtestutils framework, added
+ support for installed tests, and made the tests use the TAP
+ driver.
+
+ Some tests still need to be split up into more pieces, and
+ the debug output is now somewhat less useful in some cases.
+
+ * Fixed the test programs to not depend on local proxy
+ settings.
+
+ * Added some more checks to header-tests [#722341, Lionel
+ Landwerlin]
+
+ * Fixed the "simple-proxy" example program, which had been
+ broken for a while.
+
+ * Updated translations:
+ Korean
+
+Changes in libsoup from 2.44.1 to 2.45.3:
+
+ * The documentation has finally been updated to reflect the
+ new APIs added in 2.42.
+
+ * Added GBytes-valued :request-body-data and
+ :response-body-data properties to SoupMessage, which should
+ help some bindings.
+
+ * We now set TCP_NODELAY on sockets, improving throughput a
+ bit. In particular, this avoids an unnecessary extra round
+ trip in the TLS handshake.
+
+ * The SoupSession APIs that return GErrors now return the
+ actual underlying gio errors in the event of connection
+ failures and the like.
+
+ * Updated the copy of the Public Suffix list used by soup-tld.
+
+ * Fixed a hang with internet radio streams in Rhythmbox (and
+ some other places). [#710494, Dan]
+
+ * Fixed a connection leak when cancelling the close of
+ a message GInputStream. [#711260, Dan]
+
+ * Updated the soup_server_pause_message() /
+ soup_server_unpause_message() documentation to clarify when
+ you can and can't call them. [#710602, Philip Withnall]
+
+ * soup_message_set_request() and soup_message_set_response()
+ now g_warn_if_fail() if you pass an invalid Content-Type.
+ (And they also have better introspection annotations.)
+ [#686766, Dan]
+
+ * Plugged a few memory leaks [#711085, Christophe Fergeau]
+
+ * Fixed connection-test to pass with current glib [#711361,
+ Colin Walters]
+
+ * session-test now passes when run against the dummy TLS
+ backend (ie, if you don't have glib-networking installed),
+ as long as you have the latest glib.
+
+ * Fixed build with -Werror=format-nonliteral [#720082, Ryan
+ Lortie]
+
+ * Fix build with --without-ntlm [#710267, Dan]
+
+ * Fixed a few warnings [Dan, Fabiano Fidêncio]
+
+ * Tests are now more verbose by default under "make check",
+ since current automake just redirects all the output to a
+ log file anyway.
+
+ * Updated translations:
+ Chinese
+
+Changes in libsoup from 2.44.0 to 2.44.1:
+
+ * If you called g_input_stream_close() on SoupMessage or
+ SoupRequests's GInputStream before you finished reading it,
+ it would block until the rest of the response had been read
+ (which in the case of, eg, an infinite audio stream, meant
+ that it would block forever). This was an unintended change
+ from 2.42 and is now fixed. [#695652, Dan]
+
+ * soup_session_queue_message() on a plain SoupSession (not
+ SoupSessionAsync) was operating synchronously rather than
+ asynchronously as it should have. [#707711, Dan]
+
+ * soup-form methods now use the HTML5 x-www-form-urlencoded
+ rules rather than the HTML4 ones. (In particular, they leave
+ "-", "_", and "." unencoded.) [#708621, Alban Browaeys]
+
+ * The test programs now explicitly request the "memory"
+ GSettings backend, meaning they won't print a warning if
+ they get it accidentally, and they won't accidentally use
+ the dconf proxy settings if the dconf backend is available.
+ [Dan]
+
+ * Fix SoupSession so that if you override the proxy
+ configuration at construct time, it doesn't try to resolve
+ the default GProxyResolver. (This is important in particular
+ for programs that expect to run outside a user session,
+ which would otherwise spew errors from the dconf GSettings
+ backend about not being able to connect to dconf.) Likewise
+ with TLS settings and the TLS backend. [#708696, Dan]
+
+ * Fix the SoupServer:tls-certificate property;
+ soup_server_is_https() had not been updated to recognize it,
+ and so would return FALSE, which in turn meant that the
+ server would return 400 Bad Request in response to https
+ requests. [#709647, Fabiano Fidêncio]
+
+ * Fixed a sporadic failure in tests/connection-test. [Dan]
+
+ * Updated translations:
+ Tajik
+
+Changes in libsoup from 2.43.92 to 2.44.0:
+
+ * New/updated translations:
+ Dutch, Indonesian
+
+Changes in libsoup from 2.43.90 to 2.43.92:
+
+ * Fixed a bug in the connection-pool code, which would in
+ certain cases accidentally keep using a connection after a
+ message was cancelled while in progress, causing the next
+ request on that connection to get the response to the
+ previous request, etc. [#708006, David Woodhouse]
+
+ * Fixed some problems when falling back from samba
+ single-sign-on-based NTLM to ordinary ask-for-a-password
+ NTLM. [#703186, David Woodhouse]
+
+ * When sending a conditional GET request, SoupCache now
+ preserves the original message's list of disabled features,
+ ensure that it gets back the same kind of response the
+ original message would have. [#706338, Andre Moreira
+ Magalhaes]
+
+ * Fixed a warning when the remote host closes the connection
+ while we are writing something using chunked encoding.
+ [#703297, Dan]
+
+ * Added SoupServer:http-aliases and :https-aliases properties,
+ to specify URI schemes that should be treated as though they
+ were http (just like SoupSession:http-aliases and
+ :https-aliases, but on the server side). [#703694, Dan]
+
+ * Fixed race conditions in cache-test and timeout-test that
+ could case spurious failures. [#698305, #660581, Dan]
+
+Changes in libsoup from 2.43.5 to 2.43.90:
+
+ * Fixed the handling of unsatisfiable range requests in
+ SoupServer [pointed out on mailing list, Dan]. Also, added
+ more documentation clarifying that you don't need to handle
+ range requests yourself in many cases.
+
+ * Fixed the handling of IPv6 address literals with scope IDs.
+ (Requires the latest glib as well for the complete fix.)
+ [#669724, Dan]
+
+Changes in libsoup from 2.43.4 to 2.43.5:
+
+ * SoupProxyURIResolver is now deprecated in favor of the
+ SoupSession:proxy-resolver property (which takes a
+ GProxyResolver). [#680273, Dan]
+
+ * The SoupKnownStatusCode enum is now called SoupStatus. The
+ old name continues to exist as an alias, but is deprecated.
+ (This change has no visible effect in C; it is primarily to
+ help language bindings, so that, eg, SOUP_STATUS_NOT_FOUND
+ maps to "Soup.Status.NOT_FOUND" rather than
+ "Soup.KnownStatusCode.NOT_FOUND".) [#684409, Dan]
+
+ * Fixed the parsing of URI schemes in SoupURI (in particular,
+ to allow scheme names with digits in them). [#703776, Dan]
+
+ * Fixed SoupLogger to print a message's response headers even
+ if the message gets cancelled before the complete response
+ body is received. [#703200, Andres Gomez]
+
+ * Fixed a build problem in non-UTF-8 locales [#702534, Ross
+ Lagerwall]
+
+ * SoupSession now warns if you use
+ soup_session_pause_message() or
+ soup_session_unpause_message() on a synchronous message
+ (which has never worked, though that fact wasn't
+ documented). [#703461, Philip Withnall]
+
+Changes in libsoup from 2.43.2 to 2.43.4:
+
+ * Fixed a bug that could cause synchronous sessions to get
+ stuck in a state where no new messages would ever get
+ processed. [#703463, Philip Withnall]
+
+ * Fixed another memory leak in SoupSocket (found while
+ added a test case for #700472)
+
+ * Switched to using g_cclosure_marshal_generic() rather than
+ using glib-genmarshal. [#686042, Olivier Blin]
+
+ * Changed SoupServer to call unref() on the query hash table
+ after calling the handler, rather than destroy(), so that
+ the handler can keep a copy of the query data if it wants.
+ [#702793, Bernhard Schuster]
+
+ * Fixed a few introspection annotations
+
+ * Updated examples/get to use SoupLogger and to allow
+ redirecting the output to a file [#703231, #703229, Andres
+ Gomez]
+
+Changes in libsoup from 2.43.1 to 2.43.2:
+
+ * Fixed an authentication error when using NTLM when
+ connecting to an https site over a proxy; the code was
+ getting confused and thinking that the 200 OK response to
+ the CONNECT meant that NTLM auth had succeeded. [#698728,
+ Dan]
+
+ * Fixed a memory leak in SoupSocket. [#700472, Richard
+ Röjfors]
+
+ * Fixed a missing include error on some platforms [#700003,
+ Erik van Pienbroek]
+
+ * Fixed warnings when running against the "dummy" TLS backend.
+ [#700518, Dan]
+
+Changes in libsoup from 2.42.1 to 2.43.1:
+
+ * Including <libsoup/soup.h> no longer pulls in the system
+ networking headers. This may cause some packages to no
+ longer compile, if they were accidentally depending on this.
+ Adding "#include <gio/gnetworking.h>" will fix them on both
+ unix and Windows. (This was done as part of fixing the build
+ on Windows.) [#692134, Dan]
+
+ * Fixed SoupSession:proxy-resolver [#698163, Dan]
+
+ * Added soup_message_set_priority(), to mark messages as being
+ high, low, or normal priority, and update the message queue
+ to prioritize them accordingly. [#696277, Sergio]
+
+ * Fixed several test programs to still work if glib-networking
+ isn't installed [Dan], and fixed another to still work if
+ the kernel has no IPv6 support. [#698220, Dan]
+
+Changes in libsoup from 2.42.0 to 2.42.1:
+
+ * Fixed SoupProxyResolverDefault, which got broken in 2.42.92.
+ [#697028, Dan]
+
+ * Fixed a gigantic memory leak when using SoupCache. [#696594,
+ Xan]
+
+ * Fixed a build problem on Windows. [#696354, Kalev Lember]
+
+ * Fixed ntlm-test to pass whether or not Samba ntlm_auth
+ support was compiled in. [#697510, Dan]
+
+ * New/updated translations:
+ Basque, Turkish
+
+Changes in libsoup from 2.41.92 to 2.42.0:
+
+ * Fixed a compiler warning on 32bit in a test program. [Kalev
+ Lember]
+
+ * New/updated translations:
+ Hindi, Kannada, Korean, Malayalam, Marathi, Odia, Persian,
+ Tadjik, Tamil, Telugu
+
+Changes in libsoup from 2.41.91 to 2.41.92:
+
+ * Fixed a bug that caused libsoup to retry an incorrect
+ password repeatedly, forever, in a certain case that
+ affected Google calendars in evolution in particuar.
+ [Red Hat bug #916224, Dan]
+
+ * Also added code to make such infinite retry loops impossible
+ in the future. [Dan]
+
+ * Fixed SoupRequestData's handling of URIs with "%00" in them.
+ [#695246, Žan Doberšek]
+
+ * Added the SoupSession:proxy-resolver property, to override
+ the GProxyResolver used by a session. (This means there are
+ now three different ways of controlling proxy resolution in
+ SoupSession... this will be cleaned up a bit after 2.42.)
+ [#680273, Dan]
+
+ * Added missing G_BEGIN_DECLS/G_END_DECLS to
+ soup-message-headers.h, so that its functions can be called
+ from C++. [Carlos Garcia Campos]
+
+ * Updated translations:
+ Assamese, Belarusian, Brazilian Portuguese, Catalan
+ (Valencian), Catalan, Danish, Estonian, French, Greek,
+ Gujarati, Hungarian, Italian, Latvian, Portuguese, Russian,
+ Slovenian, Thai
+
+Changes in libsoup from 2.41.90 to 2.41.91:
+
+ * Fixed a crash that showed up with XMLRPC requests in
+ WebKitGTK. [#694920, Sergio]
+
+ * Fixed SoupCache to update the cached headers when it
+ receives a 304 Not Modified response, and added a test for
+ this. [#695121, Sergio]
+
+ * libsoup now builds under automake 1.13 (and "make check"
+ works under the parallel test harness which is the default
+ in 1.13) [#694135]
+
+ * The tests/ directory now contains only actual test programs
+ that are run by "make check", and the programs that are
+ intended more as example code are under examples/.
+
+ * New/updated translations:
+ Aragonese, Chinese (traditional), Czech, Galician, Hebrew,
+ Lithuanian, Norwegian bokmål, Punjabi, Spanish, Uyghur,
+ Vietnamese
+
+Changes in libsoup from 2.41.5 to 2.41.90:
+
+ * Added SoupSession:local-address property, which allows you
+ to force connections to bind to a particular local address
+ (eg, to control the interface that is used). [#693215, Jonh
+ Wendell]
+
+ * Fixed SoupCache to properly handle messages that get
+ cancelled, and added tests for this. [#692310, Sergio]
+
+ * Fixed a reference leak in SoupCache that resulted in
+ epiphany hanging for several seconds on exit and then
+ eventually printing "Cache flush finished despite X pending
+ requests". And added more tests. [#682527, Sergio]
+
+ * Fixed SoupAuthNTLM so that SoupSession:authenticate gets
+ emitted with retrying=TRUE if the first attempt fails (ie,
+ make it work the same way as SoupAuthBasic and
+ SoupAuthDigest). [#693222, Dan]
+
+ * Fixed the SoupSession:add-feature-by-type property to accept
+ non-SoupSessionFeature features as well (eg, auth types)
+ [Dan]
+
+ * Fixed a build bug that would break all the apache-based
+ tests if you didn't have PHP installed. [#693311, Dan]
+
+ * Updated translations:
+ Malayalam, Polish, Serbian, Slovak
+
+Changes in libsoup from 2.41.4 to 2.41.5:
+
+ * Reverted the change to SoupURI's password handling from
+ 2.41.4, since it turns out to have broken some things.
+ [#692149, Dan]
+
+ * Avoid a g_return_if_fail() when loading
+ SoupSession:ssl-ca-file fails. [#691930, Guillaume
+ Desmottes]
+
+ * Fixed a bug in SoupBodyInputStream that caused redirects in
+ WebKitGTK to hang. [#692026, Sergio]
+
+ * Updated translations:
+ Belarusian, Chinese (traditional), German, Italian,
+ Norwegian bokmål, Serbian, Uyghur
+
+Changes in libsoup from 2.41.3 to 2.41.4:
+
+ * Lots of docs fixes, including catching up with some (but not
+ all!) of the API additions and deprecations, and a new
+ chapter with hints on porting from
+ SoupSessionAsync/SoupSessionSync to the new-and-improved
+ plain SoupSession.
+
+ * The plain SoupSession type now supports
+ soup_session_queue_message() (with SoupSessionAsync
+ semantics) and soup_session_send_message() (with
+ SoupSessionSync semantics), and there are now
+ soup_session_new() and soup_session_new_with_options().
+
+ * The mirroring of the SoupMessage API onto SoupRequestHTTP,
+ added in 2.41.3, has been reverted. However, new APIs
+ soup_session_send() and soup_session_send_async() have been
+ added that let you use the GInputStream-based API with
+ SoupMessages rather than SoupRequest, so if you're doing
+ HTTP-specific stuff, you can just use that instead.
+
+ * soup_message_get_https_status() now returns the certificate
+ and flags for unsuccessful https connections as well as
+ successful ones. [#690176]
+
+ * Fixed a deadlock when calling soup_session_abort() on a
+ SoupSessionSync in some cases. [#691399]
+
+ * Internal SoupCache rewrites/improvements [#682112, Sergio]
+
+ * Plugged a memory leak in SoupCache [#690382, Sudarsana
+ Nagineni] and one in SoupAuthDigest [#690142]
+
+ * LIBSOUP_DISABLE_DEPRECATED has been renamed to
+ SOUP_DISABLE_DEPRECATED, but that's just to keep gtk-doc
+ happy, and you shouldn't use it. You should use
+ SOUP_VERSION_MIN_REQUIRED instead.
+
+ * Fixed the samba-windbind-based NTLM support, which appears
+ to have been broken before.
+
+ * SoupAuthManager is now a public class (so you can remove it
+ as a feature from a session, or disable it for a particular
+ message). It also has a new method
+ soup_auto_manager_use_auth(), which can be used to "preload"
+ authentication for a host so that libsoup will use
+ authentication on the very first request.
+
+ * SoupURI now treats "http://user@example.com" as having a
+ password of "" rather than NULL, since a NULL password would
+ not be valid for any known HTTP auth type.
+
+ * build: libsoup now uses autoreconf instead of
+ gnome-autogen.sh, and no longer uses AM_GLIB_GNU_GETTEXT
+ [Javier Jardon].
+
+ * Updated translations:
+ Assamese, Bulgarian, Estonian, Friulian, Galician, Hebrew,
+ Polish, Slovenian, Spanish
+
+Changes in libsoup from 2.41.2 to 2.41.3 (codename: "I Left My
+Deprecated APIs in A Coruña"):
+
+ * BUILD DEPENDENCY CHANGES: libsoup-gnome no longer depends on
+ libgnome-keyring, and the sqlite3 dependency has been moved
+ from libsoup-gnome to libsoup proper. (See below).
+
+
+ * SoupRequest is now stable API. SoupRequester, however, is
+ deprecated. Instead you can now call soup_session_request()
+ or soup_session_request_uri() to create a SoupRequest.
+
+ Some documentation has been updated to reflect this, but
+ much more still needs to be (in particular the "Client-side
+ Tutorial").
+
+ * SoupRequestHTTP now has a number of fields and methods that
+ mirror the SoupMessage data, so you don't have to use
+ soup_request_http_get_message() in many cases. On the flip
+ side, there is also now soup_message_get_request(). And you
+ can create a SoupRequestHTTP directly (and override its
+ request method) by using soup_session_request_http() or
+ soup_session_request_http_uri()).
+
+ * soup_message_set_chunk_allocator() is now deprecated; apps
+ that want to do streaming reads should just use SoupRequest,
+ which is vastly more sane.
+
+
+ * SoupPasswordManager is now deprecated, and
+ SoupPasswordManagerGNOME is now a no-op (and libsoup-gnome
+ no longer links against libgnome-keyring). [#594377, #679866]
+
+ * SoupCookieJarSqlite is now deprecated in favor of
+ SoupCookieJarDB, which is exactly the same thing except that
+ it's in libsoup itself rather than being in libsoup-gnome
+ (something that many people have requested). This means that
+ libsoup now requires sqlite3... if this offends you horribly
+ then you have a few months to speak up...
+
+ * SoupProxyResolverGNOME is now deprecated; there hasn't been
+ any real reason to use it since SoupProxyResolverDefault was
+ added.
+
+ * As a result of the last three items, libsoup-gnome now
+ consists entirely of deprecated APIs, and there is no reason
+ you should use it any more (though packagers need to keep
+ building it, for backward compatibility).
+
+
+ * SoupSession is no longer an abstract class, and you can
+ create a plain SoupSession, which behaves in a more
+ traditionally-gio-like way (allowing a mix of sync and async
+ methods, etc). This "plain" SoupSession also has more sane
+ default values of certain properties, and has certain
+ SoupSessionFeatures built in.
+
+ This will eventually replace SoupSessionAsync and
+ SoupSessionSync completely, but most of the documentation
+ hasn't yet been updated at this point...
+
+ This change involved merging the majority of the
+ SoupSessionAsync and SoupSessionSync code into SoupSession,
+ getting rid of lots of redundancy in the process. There may
+ be some bug fallout from this (probably on the
+ SoupSessionSync side, since WebKit's tests tend to shake out
+ all SoupSessionAsync bugs). However, this should help to
+ avoid SoupSessionSync-only bugs in the future, since much
+ more of the code is now shared.
+
+
+ * Usernames and passwords passed into SoupSession a URI will
+ now be cleared after they're used, so that if they are
+ wrong, the authenticate signal will be emitted on the next
+ round. [#689673, Martin Robinson]
+
+ * SoupURI now leaves "%00" in URIs as-is, rather than decoding
+ it to "\0", which was not intended and is never useful.
+
+ * Fixed a bug in SoupBodyOutputStream that could cause libsoup
+ to sometimes use blocking I/O rather than non-blocking when
+ writing chunked message bodies. [#688974, Milan Plzik]
+
+ * Fixed a bug in SoupFilterInputStream that could cause some
+ non-blocking reads to suck up CPU while waiting for the
+ network. (This was noticed with multipart/x-mixed-replace
+ processing; it's not clear if it affected anything else.)
+ [Gustavo]
+
+ * tests: misc small fixes
+
+
+ * New/updated translations:
+ Assamese, Galician, Japanese, Odia, Polish, Spanish
+
+Changes in libsoup from 2.41.1 to 2.41.2:
+
+ * libsoup-2.4.so and libsoup-gnome-2.4.so now only export the
+ symbols that are part of the public API. (Plus, temporarily,
+ one additional symbol, soup_message_io_cleanup, which is
+ used by gvfs and possibly a few other modules, even though
+ it shouldn't be. If you copied soup-input-stream.c from
+ gvfs, you should port your code to use SoupRequest instead.)
+ [Dan, #595176]
+
+ * Added SOUP_VERSION_MIN_REQUIRED / SOUP_VERSION_MAX_ALLOWED
+ macros like the corresponding glib ones, to allow libsoup
+ users to request per-version deprecation/availability
+ warnings. [Dan]
+
+ * Fixed a crash caused by a race condition in SoupSessionSync,
+ and reorganized some code to avoid other possible similar
+ race conditions. [#684238, Dan]
+
+ * Fixed a crash when a DNS resolution failed, caused by a bug
+ in the GTask porting in 2.41.1. [#688330, Milan Crha]
+
+ * Fixed a problem that would cause g_warning()s in epiphany
+ when browsing sites that (incorrectly) returned empty
+ Cache-Control headers. [#683433, Dan]
+
+ * We now add a Host header to HTTP/1.0 requests as well as
+ HTTP/1.1 ones. [Dan]
+
+ * Fixed a bug in the printing of IPv6 address literals in the
+ Host header in SoupLogger. (They were being sent across the
+ wire correctly, they were just printed wrong by SoupLogger.)
+ [Dan]
+
+ * Belatedly added soup-multipart-input-stream.h to soup.h.
+ [Dan]
+
+ * Removed an evil hack in the long-deprecated
+ SoupProxyResolver code (not to be confused with
+ SoupProxyURIResolver), to avoid warnings with glib master.
+ If you had previously implemented a custom
+ SoupProxyResolver, then it will probably (silently) stop
+ working, but you should have ported it to
+ SoupProxyURIResolver years ago anyway... [Dan, #687659]
+
+ * Fixed a few race conditions in the test programs that could
+ cause "make check" to fail on slow or heavily-loaded
+ machines. [Dan]
+
+ * Further cleaned up and reorganized the internal HTTP I/O
+ codepaths, in preparation for an improved SoupCache.
+ [#682112, Sergio] (This change should not actually be
+ externally noticeable. But if SoupContentDecoder or
+ SoupContentSniffer turns out to be broken in this release,
+ this would be why).
+
+ * New/updated translations:
+ Slovak, Uyghur
+
+Changes in libsoup from 2.40.1 to 2.41.1:
+
+ * Changed the behavior of NTLM authentication to be more like
+ what other apps apparently do. Now if the user does not
+ specify a domain in the username (eg, "MYDOMAIN\username"),
+ then we will not specify a domain in the NTLM response
+ (rather than assuming that the user is in the server's
+ default domain). People who get broken by this change should
+ be able to fix it by including an explicit domain in their
+ username, but the theory is that no one should get broken by
+ this... If this change does turn out to hurt more than it
+ helps then it may be reverted later. [#624613, Dan, based on
+ a suggestion from David Woodhouse]
+
+ * Fixed a crash caused by a race condition in SoupSessionSync.
+ [#684238, Dan]
+
+ * SoupRequest now supports resource:// URIs, for reading from
+ gresource. [#682721, Carlos]
+
+ * Added new compile-time and runtime APIs for checking the
+ libsoup version. [#684514, Martin Robinson]
+
+ * Updated to take advantage of (and require) glib 2.35;
+ removed all g_type_init() calls and ported to GTask. [Dan]
+
+ * Added support for Apache 2.4 to the unit tests. [Dan]
+
+ * New translations:
+ Uzbek (Cyrillic)
+
+Changes in libsoup from 2.40.0 to 2.40.1:
+
+ * Improved the parsing of multipart/x-mixed-replace responses.
+ [#685752, Gustavo]
+
+ * Fixed handling of IPv6 address literals. [#684990, Dan]
+
+ * New/updated translations:
+ Catalan, Catalan (Valencian), Norwegian bokmål
+
+Changes in libsoup from 2.39.92 to 2.40.0:
+
+ * New/updated translations:
+ Bengali (India), Bulgarian, Chinese (Simplified), Estonian,
+ German, Hebrew, Hindi, Hungarian, Latvian, Malayalam,
+ Punjabi, Tamil, Telugu, Thai, Ukranian
+
+Changes in libsoup from 2.39.91 to 2.39.92:
+
+ * Fixed some g_warnings (and a possible crash) with the
+ soup_request_send_async(). [#683404, Dan]
+
+ * Fixed a hang with SoupSessionSync [#682923, Dan]
+
+ * Handle empty "Cache-Control" headers. [Sergio]
+
+ * New/updated translations:
+ Assamese, Belarusian, Brazilian Portuguese, British English,
+ Chinese (Traditional), Czech, Danish, French, Galician,
+ Greek, Gujarati, Hungarian, Indonesian, Italian, Lithuanian,
+ Marathi, Persian, Polish, Portuguese, Russian, Serbian,
+ Slovenian, Spanish
+
+Changes in libsoup from 2.39.90 to 2.39.91:
+
+ * Added missing (transfer full) annotation to
+ soup_cookie_jar_add_cookie() and
+ soup_cookie_jar_add_cookie_with_first_party(), fixing
+ crashes in bindings. [#682554, Daniel Drake]
+
+ * Fixed a crash [#682569, Alexander Larsson] and a win32 build
+ problem [#683200, Kalev Lember] introduced by the memory
+ leak fixes in 2.39.90
+
+ * Fixed the SoupMessage:network-event signal, which had been
+ skipping the G_SOCKET_CLIENT_COMPLETE state in tunneled
+ https connections. And added a test for this. [Dan]
+
+ * New/updated translations:
+ French, Japanese, Korean, Latvian, Lithuanian, Polish,
+ Punjabi, Swedish, Tamil
+
+Changes in libsoup from 2.39.5 to 2.39.90:
+
+ * Added SoupMultipartInputStream, for handling multipart
+ responses (particularly multipart/x-mixed-replace).
+ [#656684, Gustavo]
+
+ * Fixed a potential crash in SoupSessionAsync after the
+ session is finalized.
+
+ * Fixed a regression in soup_tls_is_public_suffix() [#681085,
+ Sergio]
+
+ * Added a SOUP_MESSAGE_IDEMPOTENT flag, so that apps can
+ bypass the "POSTs must be sent on new connections" check,
+ which was causing evolution-ews to have to create a new
+ connection for every request. [#681493, Milan Crha]
+
+ * Changed SoupSession so that pending SoupMessages now hold a
+ ref on the session. It is possible that this will break code
+ that was depending on the old, dumb, behavior (where
+ unreffing the session with messages pending would cause
+ those messages to be cancelled), in which case this will be
+ reverted before 2.40.
+
+ * Fixed memory leaks found by valgrind.
+
+ * Cleaned up some code in SoupCache. [#681509, Sergio]
+
+ * New/updated translations:
+ Bengali (India), Chinese (Traditional), German, Marathi,
+ Turkish, Ukranian
+
+Changes in libsoup from 2.39.4.1 to 2.39.5:
+
+ * Fixed several bugs in the soup-message-io updates that could
+ cause hangs or I/O errors. [#679527 and other bugs not filed
+ in bugzilla]
+
+ * Fixed SoupServer:async-context to work properly again
+
+ * Further fixes to soup_uri_normalize() when using the
+ "unescape_extra" parameter. [#680018]
+
+ * Fixed soup_xmlrpc_parse_method_call() to handle the case
+ where there is no <params> element (which is legal).
+ [#671661]
+
+ * Fixed the deprecation warning on soup_message_headers_get()
+ [#680143]
+
+ * Added warnings to some erroneous SoupSocket usages rather
+ than returning bogus data. [#673083, Simon McVittie]
+
+ * Fixed build under Windows/MinGW
+
+ * SoupSocket no longer emits the "readable" signal when a
+ socket is disconnected if that socket is non-blocking.
+
+ * Updated public suffix list to the current version.
+
+ * New/Updated translations:
+ Assamese, Chinese (simplified), Esperanto, Galician, Greek, Gujarati,
+ Hebrew, Norwegian bokmål, Russian, Serbian, Slovenian
+
Changes in libsoup from 2.39.4 to 2.39.4.1:
* Fixed indentation problems in tld-parser.py so it will work
diff --git a/autogen.sh b/autogen.sh
index a329db5b..cb3e9bdb 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,21 +1,33 @@
#!/bin/sh
# Run this to generate all the initial makefiles, etc.
-srcdir=`dirname $0`
-test -z "$srcdir" && srcdir=.
+test -n "$srcdir" || srcdir=`dirname "$0"`
+test -n "$srcdir" || srcdir=.
-PKG_NAME="libsoup"
+olddir=`pwd`
+cd $srcdir
-(test -f $srcdir/configure.ac \
- && test -f $srcdir/libsoup.doap \
- && test -d $srcdir/libsoup) || {
- echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
- echo " top-level $PKG_NAME directory"
- exit 1
-}
+AUTORECONF=`which autoreconf`
+if test -z $AUTORECONF; then
+ echo "*** No autoreconf found, please intall it ***"
+ exit 1
+fi
-which gnome-autogen.sh || {
- echo "You need to install gnome-common from the GNOME CVS"
- exit 1
-}
-USE_GNOME2_MACROS=1 . gnome-autogen.sh
+INTLTOOLIZE=`which intltoolize`
+if test -z $INTLTOOLIZE; then
+ echo "*** No intltoolize found, please install the intltool package ***"
+ exit 1
+fi
+
+GTKDOCIZE=`which gtkdocize`
+if test -z $GTKDOCIZE; then
+ echo "*** No GTK-Doc found, please install it ***"
+ exit 1
+fi
+
+gtkdocize || exit $?
+intltoolize --automake --copy
+autoreconf --force --install --verbose
+
+cd $olddir
+test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
diff --git a/configure.ac b/configure.ac
index 47daa7dd..96bb378a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,24 +2,36 @@ dnl *******************************************
dnl *** Initialize automake and set version ***
dnl *******************************************
+m4_define([soup_major_version], [2])
+m4_define([soup_minor_version], [46])
+m4_define([soup_micro_version], [0])
+
AC_PREREQ(2.63)
-AC_INIT([libsoup],[2.39.4.1],[http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup])
+AC_INIT([libsoup],[soup_major_version.soup_minor_version.soup_micro_version],[http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup])
AC_CONFIG_SRCDIR([libsoup-2.4.pc.in])
AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz -Wno-portability])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
AC_PROG_MAKE_SET
+SOUP_MAJOR_VERSION=soup_major_version
+SOUP_MINOR_VERSION=soup_minor_version
+SOUP_MICRO_VERSION=soup_micro_version
+AC_SUBST(SOUP_MAJOR_VERSION)
+AC_SUBST(SOUP_MINOR_VERSION)
+AC_SUBST(SOUP_MICRO_VERSION)
+
SOUP_API_VERSION=2.4
AC_SUBST(SOUP_API_VERSION)
# Increment on interface addition. Reset on removal.
-SOUP_AGE=5
+SOUP_AGE=7
# Increment on interface add, remove, or change.
-SOUP_CURRENT=6
+SOUP_CURRENT=8
# Increment on source change. Reset when CURRENT changes.
SOUP_REVISION=0
@@ -52,12 +64,6 @@ fi
AC_SUBST(SOUP_DEBUG_FLAGS)
-# Set the maintainer flags
-#if test -d .git; then
-# SOUP_MAINTAINER_FLAGS="-DG_DISABLE_DEPRECATED"
-#fi
-AC_SUBST(SOUP_MAINTAINER_FLAGS)
-
dnl ***************************
dnl *** Checks for programs ***
dnl ***************************
@@ -72,20 +78,26 @@ dnl ***********************
dnl *** Checks for glib ***
dnl ***********************
-GLIB_REQUIRED=2.33.1
+GLIB_REQUIRED=2.38.0
AM_PATH_GLIB_2_0($GLIB_REQUIRED,,,gobject gio)
if test "$GLIB_LIBS" = ""; then
AC_MSG_ERROR(GLIB $GLIB_REQUIRED or later is required to build libsoup)
fi
-GLIB_CFLAGS="$GLIB_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_34"
+GLIB_CFLAGS="$GLIB_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_38"
GLIB_MAKEFILE='$(top_srcdir)/Makefile.glib'
AC_SUBST(GLIB_MAKEFILE)
+GLIB_TESTS
+
PKG_CHECK_MODULES(XML, libxml-2.0)
AC_SUBST(XML_CFLAGS)
AC_SUBST(XML_LIBS)
+PKG_CHECK_MODULES(SQLITE, sqlite3)
+AC_SUBST(SQLITE_CFLAGS)
+AC_SUBST(SQLITE_LIBS)
+
dnl ***********************
dnl *** Check for Win32 ***
dnl ***********************
@@ -110,14 +122,11 @@ IT_PROG_INTLTOOL([0.35.0])
GETTEXT_PACKAGE=libsoup
AC_SUBST([GETTEXT_PACKAGE])
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[The gettext domain name])
-AM_GLIB_GNU_GETTEXT
dnl *******************
dnl *** Misc checks ***
dnl *******************
AC_CHECK_FUNCS(gmtime_r)
-AC_CHECK_FUNCS(mmap)
-AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket))
dnl *********************
dnl *** GNOME support ***
@@ -128,32 +137,12 @@ AC_ARG_WITH(gnome,
:, [if test $os_win32 = yes; then with_gnome=no; else with_gnome=yes; fi])
AC_MSG_RESULT($with_gnome)
-if test $with_gnome != no -a $os_win32 != yes; then
- PKG_CHECK_MODULES(GNOME_KEYRING, gnome-keyring-1, :,
- AC_MSG_ERROR(
-[Could not find gnome-keyring devel files.
-Configure with --without-gnome if you wish to build only libsoup
-without GNOME-specific features.]))
-fi
-AC_SUBST(GNOME_KEYRING_CFLAGS)
-AC_SUBST(GNOME_KEYRING_LIBS)
-
AM_CONDITIONAL(BUILD_LIBSOUP_GNOME, test $with_gnome != no)
if test $with_gnome != no; then
AC_DEFINE(HAVE_GNOME, 1, [Defined if GNOME support is enabled])
-
- PKG_CHECK_MODULES(SQLITE, sqlite3, :, [AC_MSG_ERROR(dnl
-[Could not find sqlite3 devel files:
-
-$SQLITE_PKG_ERRORS
-
-Pass "--without-gnome" to configure if you want to build libsoup
-without GNOME support.])])
fi
AC_SUBST(HAVE_GNOME)
-AC_SUBST(SQLITE_CFLAGS)
-AC_SUBST(SQLITE_LIBS)
dnl ***************
@@ -172,7 +161,7 @@ save_LIBS="$LIBS"
CFLAGS="$CFLAGS $GLIB_CFLAGS"
LIBS="$LIBS $GLIB_LIBS"
AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <gio/gio.h>],
- [g_type_init (); return !g_tls_backend_supports_tls (g_tls_backend_get_default ());])],
+ [return !g_tls_backend_supports_tls (g_tls_backend_get_default ());])],
[have_glib_networking=yes],
[have_glib_networking=no],
[have_glib_networking="unknown (cross-compiling)"])
@@ -195,11 +184,6 @@ dnl ******************************
dnl *** Stuff for regression tests
dnl ******************************
AC_MSG_NOTICE([checking for programs needed for regression tests])
-MISSING_REGRESSION_TEST_PACKAGES=""
-
-if test "$have_glib_networking" = "no"; then
- MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES glib-networking"
-fi
AC_ARG_WITH(apache-httpd,
AS_HELP_STRING([--with-apache-httpd], [Path to apache httpd (for tests)]),
@@ -211,6 +195,11 @@ if test "$APACHE_HTTPD" != "no"; then
case $apache_version in
2.2.*)
AC_MSG_RESULT([$apache_version (ok)])
+ apache_version=2.2
+ ;;
+ 2.4.*)
+ AC_MSG_RESULT([$apache_version (ok)])
+ apache_version=2.4
;;
*)
AC_MSG_RESULT([$apache_version (ignoring)])
@@ -227,7 +216,7 @@ if test "$APACHE_HTTPD" != "no"; then
AS_HELP_STRING([--with-apache-module-dir], [Apache modules dirs (for tests)]),
apache_module_dirs="$withval",
[apache_prefix=`dirname \`dirname $APACHE_HTTPD\``
- mpm=`$APACHE_HTTPD -V | sed -ne 's/^Server MPM: */-/p' | tr 'A-Z' 'a-z'`
+ mpm=`$APACHE_HTTPD -V -C "ServerName localhost" | sed -ne 's/^Server MPM: */-/p' | tr 'A-Z' 'a-z'`
# This only works with bash, but should fail harmlessly in sh
apache_module_dirs=`echo $apache_prefix/lib{64,}/{apache,apache2,http,http2,httpd}{$mpm,}{/modules,}`])
for dir in $apache_module_dirs; do
@@ -256,19 +245,18 @@ fi
if test "$APACHE_HTTPD" != "no" -a -n "$APACHE_MODULE_DIR" -a -n "$APACHE_SSL_MODULE_DIR"; then
AC_DEFINE(HAVE_APACHE, 1, [Whether or not apache can be used for tests])
- have_apache=1
- if test -z "$APACHE_PHP_MODULE_DIR"; then
- MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES mod_php5"
+ if test $apache_version = 2.2; then
+ AC_DEFINE(HAVE_APACHE_2_2, 1, [Apache is 2.2.x])
+ else
+ AC_DEFINE(HAVE_APACHE_2_4, 1, [Apache is 2.4.x])
fi
+ have_apache=1
else
have_apache=0
- if test "$APACHE_HTTPD" = "no" -o -z "$APACHE_MODULE_DIR"; then
- MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES apache"
- else
- MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES mod_ssl"
- fi
fi
-AM_CONDITIONAL(HAVE_APACHE, test $have_apache = 1)
+AM_CONDITIONAL(HAVE_APACHE, test "$have_apache" = 1)
+AM_CONDITIONAL(HAVE_APACHE_2_2, test "$apache_version" = 2.2)
+AM_CONDITIONAL(HAVE_APACHE_2_4, test "$apache_version" = 2.4)
if test "$have_apache" = 1; then
AC_CHECK_PROGS(PHP, php php5)
@@ -279,36 +267,27 @@ if test "$have_apache" = 1; then
IF_HAVE_PHP=""
else
have_php=no
- IF_HAVE_PHP="#"
- MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES php5"
+ IF_HAVE_PHP="\#"
fi
AC_MSG_RESULT($have_php)
+ AC_SUBST(IF_HAVE_PHP)
if test "$have_php" = yes; then
- AC_MSG_CHECKING([for xmlrpc-epi-php])
+ AC_MSG_CHECKING([for php-xmlrpc])
if $PHP --rf xmlrpc_server_create | grep -q "does not exist"; then
- have_xmlrpc_epi_php=no
- MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES php-xmlrpc"
+ have_php_xmlrpc=no
else
- have_xmlrpc_epi_php=yes
+ have_php_xmlrpc=yes
+ AC_DEFINE(HAVE_PHP_XMLRPC, 1, [Have php-xmlrpc])
fi
- AC_MSG_RESULT($have_xmlrpc_epi_php)
+ AC_MSG_RESULT($have_php_xmlrpc)
fi
fi
-AC_SUBST(IF_HAVE_PHP)
-AM_CONDITIONAL(HAVE_XMLRPC_EPI_PHP, test "$have_xmlrpc_epi_php" = yes)
-
AC_PATH_PROG(CURL, curl, no)
if test "$CURL" != no; then
AC_DEFINE(HAVE_CURL, 1, [Whether or not curl can be used for tests])
-else
- MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES curl"
fi
-AM_CONDITIONAL(HAVE_CURL, test "$CURL" != no)
-
-AC_SUBST(MISSING_REGRESSION_TEST_PACKAGES)
-AM_CONDITIONAL(MISSING_REGRESSION_TEST_PACKAGES, test -n "$MISSING_REGRESSION_TEST_PACKAGES")
dnl **********************************************************
dnl *** path of NTLM single-sign-on helper ntlm_auth
@@ -341,6 +320,7 @@ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
-Wall -Wstrict-prototypes -Werror=missing-prototypes \
-Werror=implicit-function-declaration \
-Werror=pointer-arith -Werror=init-self -Werror=format=2 \
+ -Wno-format-zero-length \
-Werror=missing-include-dirs -Werror=aggregate-return \
-Werror=declaration-after-statement"
fi
@@ -359,15 +339,9 @@ AC_CONFIG_FILES([
po/Makefile.in
po/Makefile
tests/Makefile
- tests/httpd.conf
+ examples/Makefile
docs/Makefile
docs/reference/Makefile
+ libsoup/soup-version.h
])
AC_OUTPUT
-
-if test -n "$MISSING_REGRESSION_TEST_PACKAGES"; then
- echo ""
- echo Some regression tests will not be run due to missing packages:
- echo $MISSING_REGRESSION_TEST_PACKAGES
- echo ""
-fi
diff --git a/data/effective_tld_names.dat b/data/effective_tld_names.dat
index 0df8800d..95a555aa 100644
--- a/data/effective_tld_names.dat
+++ b/data/effective_tld_names.dat
@@ -1,44 +1,6 @@
-// ***** BEGIN LICENSE BLOCK *****
-// Version: MPL 1.1/GPL 2.0/LGPL 2.1
-//
-// The contents of this file are subject to the Mozilla Public License Version
-// 1.1 (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-// http://www.mozilla.org/MPL/
-//
-// Software distributed under the License is distributed on an "AS IS" basis,
-// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-// for the specific language governing rights and limitations under the
-// License.
-//
-// The Original Code is the Public Suffix List.
-//
-// The Initial Developer of the Original Code is
-// Jo Hermans <jo.hermans@gmail.com>.
-// Portions created by the Initial Developer are Copyright (C) 2007
-// the Initial Developer. All Rights Reserved.
-//
-// Contributor(s):
-// Ruben Arakelyan <ruben@rubenarakelyan.com>
-// Gervase Markham <gerv@gerv.net>
-// Pamela Greene <pamg.bugs@gmail.com>
-// David Triendl <david@triendl.name>
-// Jothan Frakes <jothan@gmail.com>
-// The kind representatives of many TLD registries
-//
-// Alternatively, the contents of this file may be used under the terms of
-// either the GNU General Public License Version 2 or later (the "GPL"), or
-// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-// in which case the provisions of the GPL or the LGPL are applicable instead
-// of those above. If you wish to allow use of your version of this file only
-// under the terms of either the GPL or the LGPL, and not to allow others to
-// use your version of this file under the terms of the MPL, indicate your
-// decision by deleting the provisions above and replace them with the notice
-// and other provisions required by the GPL or the LGPL. If you do not delete
-// the provisions above, a recipient may use your version of this file under
-// the terms of any one of the MPL, the GPL or the LGPL.
-//
-// ***** END LICENSE BLOCK *****
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// ===BEGIN ICANN DOMAINS===
@@ -213,17 +175,16 @@ it.ao
// aq : http://en.wikipedia.org/wiki/.aq
aq
-// ar : http://en.wikipedia.org/wiki/.ar
-*.ar
-!congresodelalengua3.ar
-!educ.ar
-!gobiernoelectronico.ar
-!mecon.ar
-!nacion.ar
-!nic.ar
-!promocion.ar
-!retina.ar
-!uba.ar
+// ar : https://nic.ar/normativa-vigente.xhtml
+ar
+com.ar
+edu.ar
+gob.ar
+int.ar
+mil.ar
+net.ar
+org.ar
+tur.ar
// arpa : http://en.wikipedia.org/wiki/.arpa
// Confirmed by registry <iana-questions@icann.org> 2008-06-18
@@ -252,18 +213,18 @@ or.at
// au : http://en.wikipedia.org/wiki/.au
// http://www.auda.org.au/
// 2LDs
-com.au
-net.au
-org.au
-edu.au
-gov.au
-csiro.au
-asn.au
-id.au
-// Historic 2LDs (closed to new registration, but sites still exist)
-info.au
-conf.au
-oz.au
+com.au
+net.au
+org.au
+edu.au
+gov.au
+asn.au
+id.au
+csiro.au
+// Historic 2LDs (closed to new registration, but sites still exist)
+info.au
+conf.au
+oz.au
// CGDNs - http://www.cgdn.org.au/
act.au
nsw.au
@@ -283,9 +244,8 @@ tas.edu.au
vic.edu.au
wa.edu.au
act.gov.au
-// Removed at request of Shae.Donelan@services.nsw.gov.au, 2010-03-04
-// nsw.gov.au
-nt.gov.au
+// nsw.gov.au Bug 547985 - Removed at request of <Shae.Donelan@services.nsw.gov.au>
+// nt.gov.au Bug 940478 - Removed at request of Greg Connors <Greg.Connors@nt.gov.au>
qld.gov.au
sa.gov.au
tas.gov.au
@@ -388,7 +348,7 @@ z.bg
6.bg
7.bg
8.bg
-9.bg
+9.bg
// bh : http://en.wikipedia.org/wiki/.bh
bh
@@ -440,7 +400,7 @@ mil.bo
tv.bo
// br : http://registro.br/dominio/dpn.html
-// Updated by registry <fneves@registro.br> 2011-03-01
+// Submitted by registry <fneves@registro.br> 2011-03-01
br
adm.br
adv.br
@@ -453,13 +413,13 @@ b.br
bio.br
blog.br
bmd.br
-can.br
cim.br
cng.br
cnt.br
com.br
coop.br
ecn.br
+eco.br
edu.br
emp.br
eng.br
@@ -480,6 +440,7 @@ ind.br
inf.br
jor.br
jus.br
+leg.br
lel.br
mat.br
med.br
@@ -729,6 +690,14 @@ inf.cu
// cv : http://en.wikipedia.org/wiki/.cv
cv
+// cw : http://www.una.cw/cw_registry/
+// Confirmed by registry <registry@una.net> 2013-03-26
+cw
+com.cw
+edu.cw
+net.cw
+org.cw
+
// cx : http://en.wikipedia.org/wiki/.cx
// list of other 2nd level tlds ?
cx
@@ -817,7 +786,7 @@ org.ee
fie.ee
// eg : http://en.wikipedia.org/wiki/.eg
-eg
+eg
com.eg
edu.eg
eun.eg
@@ -916,13 +885,12 @@ pvt.ge
// gf : http://en.wikipedia.org/wiki/.gf
gf
-// gg : http://www.channelisles.net/applic/avextn.shtml
+// gg : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
gg
co.gg
-org.gg
net.gg
-sch.gg
-gov.gg
+org.gg
// gh : http://en.wikipedia.org/wiki/.gh
// see also: http://www.nic.gh/reg_now.php
@@ -987,9 +955,15 @@ gov.gr
// gs : http://en.wikipedia.org/wiki/.gs
gs
-// gt : http://www.gt/politicas.html
-*.gt
-!www.gt
+// gt : http://www.gt/politicas_de_registro.html
+gt
+com.gt
+edu.gt
+gob.gt
+ind.gt
+mil.gt
+net.gt
+org.gt
// gu : http://gadao.gov.gu/registration.txt
*.gu
@@ -1027,7 +1001,7 @@ org.hk
网絡.hk
组织.hk
組織.hk
-組织.hk
+組织.hk
// hm : http://en.wikipedia.org/wiki/.hm
hm
@@ -1103,13 +1077,14 @@ tozsde.hu
utazas.hu
video.hu
-// id : http://en.wikipedia.org/wiki/.id
-// see also: https://register.pandi.or.id/
+// id : https://register.pandi.or.id/
id
ac.id
+biz.id
co.id
go.id
mil.id
+my.id
net.id
or.id
sch.id
@@ -1122,16 +1097,18 @@ gov.ie
// il : http://en.wikipedia.org/wiki/.il
*.il
-// im : https://www.nic.im/pdfs/imfaqs.pdf
+// im : https://www.nic.im/
+// Submitted by registry <info@nic.im> 2013-11-15
im
+ac.im
co.im
+com.im
ltd.co.im
-plc.co.im
net.im
-gov.im
org.im
-nic.im
-ac.im
+plc.co.im
+tt.im
+tv.im
// in : http://en.wikipedia.org/wiki/.in
// see also: http://www.inregistry.in/policies/
@@ -1203,9 +1180,9 @@ int.is
it
gov.it
edu.it
-// list of reserved geo-names :
+// list of reserved geo-names :
// http://www.nic.it/documenti/regolamenti-e-linee-guida/regolamento-assegnazione-versione-6.0.pdf
-// (There is also a list of reserved geo-names corresponding to Italian
+// (There is also a list of reserved geo-names corresponding to Italian
// municipalities : http://www.nic.it/documenti/appendice-c.pdf , but it is
// not included here.)
agrigento.it
@@ -1484,13 +1461,12 @@ vi.it
viterbo.it
vt.it
-// je : http://www.channelisles.net/applic/avextn.shtml
+// je : http://www.channelisles.net/register-domains/
+// Confirmed by registry <nigel@channelisles.net> 2013-11-28
je
co.je
-org.je
net.je
-sch.je
-gov.je
+org.je
// jm : http://www.com.jm/register.html
*.jm
@@ -1511,10 +1487,9 @@ jobs
// jp : http://en.wikipedia.org/wiki/.jp
// http://jprs.co.jp/en/jpdomain.html
-// Submitted by registry <yone@jprs.co.jp> 2008-06-11
-// Updated by registry <yone@jprs.co.jp> 2008-12-04
+// Submitted by registry <info@jprs.jp> 2012-05-28
jp
-// jp organizational type names
+// jp organizational type names
ac.jp
ad.jp
co.jp
@@ -1524,125 +1499,1750 @@ gr.jp
lg.jp
ne.jp
or.jp
+// jp preficture type names
+aichi.jp
+akita.jp
+aomori.jp
+chiba.jp
+ehime.jp
+fukui.jp
+fukuoka.jp
+fukushima.jp
+gifu.jp
+gunma.jp
+hiroshima.jp
+hokkaido.jp
+hyogo.jp
+ibaraki.jp
+ishikawa.jp
+iwate.jp
+kagawa.jp
+kagoshima.jp
+kanagawa.jp
+kochi.jp
+kumamoto.jp
+kyoto.jp
+mie.jp
+miyagi.jp
+miyazaki.jp
+nagano.jp
+nagasaki.jp
+nara.jp
+niigata.jp
+oita.jp
+okayama.jp
+okinawa.jp
+osaka.jp
+saga.jp
+saitama.jp
+shiga.jp
+shimane.jp
+shizuoka.jp
+tochigi.jp
+tokushima.jp
+tokyo.jp
+tottori.jp
+toyama.jp
+wakayama.jp
+yamagata.jp
+yamaguchi.jp
+yamanashi.jp
// jp geographic type names
// http://jprs.jp/doc/rule/saisoku-1.html
-*.aichi.jp
-*.akita.jp
-*.aomori.jp
-*.chiba.jp
-*.ehime.jp
-*.fukui.jp
-*.fukuoka.jp
-*.fukushima.jp
-*.gifu.jp
-*.gunma.jp
-*.hiroshima.jp
-*.hokkaido.jp
-*.hyogo.jp
-*.ibaraki.jp
-*.ishikawa.jp
-*.iwate.jp
-*.kagawa.jp
-*.kagoshima.jp
-*.kanagawa.jp
*.kawasaki.jp
*.kitakyushu.jp
*.kobe.jp
-*.kochi.jp
-*.kumamoto.jp
-*.kyoto.jp
-*.mie.jp
-*.miyagi.jp
-*.miyazaki.jp
-*.nagano.jp
-*.nagasaki.jp
*.nagoya.jp
-*.nara.jp
-*.niigata.jp
-*.oita.jp
-*.okayama.jp
-*.okinawa.jp
-*.osaka.jp
-*.saga.jp
-*.saitama.jp
*.sapporo.jp
*.sendai.jp
-*.shiga.jp
-*.shimane.jp
-*.shizuoka.jp
-*.tochigi.jp
-*.tokushima.jp
-*.tokyo.jp
-*.tottori.jp
-*.toyama.jp
-*.wakayama.jp
-*.yamagata.jp
-*.yamaguchi.jp
-*.yamanashi.jp
*.yokohama.jp
-!metro.tokyo.jp
-!pref.aichi.jp
-!pref.akita.jp
-!pref.aomori.jp
-!pref.chiba.jp
-!pref.ehime.jp
-!pref.fukui.jp
-!pref.fukuoka.jp
-!pref.fukushima.jp
-!pref.gifu.jp
-!pref.gunma.jp
-!pref.hiroshima.jp
-!pref.hokkaido.jp
-!pref.hyogo.jp
-!pref.ibaraki.jp
-!pref.ishikawa.jp
-!pref.iwate.jp
-!pref.kagawa.jp
-!pref.kagoshima.jp
-!pref.kanagawa.jp
-!pref.kochi.jp
-!pref.kumamoto.jp
-!pref.kyoto.jp
-!pref.mie.jp
-!pref.miyagi.jp
-!pref.miyazaki.jp
-!pref.nagano.jp
-!pref.nagasaki.jp
-!pref.nara.jp
-!pref.niigata.jp
-!pref.oita.jp
-!pref.okayama.jp
-!pref.okinawa.jp
-!pref.osaka.jp
-!pref.saga.jp
-!pref.saitama.jp
-!pref.shiga.jp
-!pref.shimane.jp
-!pref.shizuoka.jp
-!pref.tochigi.jp
-!pref.tokushima.jp
-!pref.tottori.jp
-!pref.toyama.jp
-!pref.wakayama.jp
-!pref.yamagata.jp
-!pref.yamaguchi.jp
-!pref.yamanashi.jp
-!city.chiba.jp
-!city.fukuoka.jp
-!city.hiroshima.jp
!city.kawasaki.jp
!city.kitakyushu.jp
!city.kobe.jp
-!city.kyoto.jp
!city.nagoya.jp
-!city.niigata.jp
-!city.okayama.jp
-!city.osaka.jp
-!city.saitama.jp
!city.sapporo.jp
!city.sendai.jp
-!city.shizuoka.jp
!city.yokohama.jp
+// 4th level registration
+aisai.aichi.jp
+ama.aichi.jp
+anjo.aichi.jp
+asuke.aichi.jp
+chiryu.aichi.jp
+chita.aichi.jp
+fuso.aichi.jp
+gamagori.aichi.jp
+handa.aichi.jp
+hazu.aichi.jp
+hekinan.aichi.jp
+higashiura.aichi.jp
+ichinomiya.aichi.jp
+inazawa.aichi.jp
+inuyama.aichi.jp
+isshiki.aichi.jp
+iwakura.aichi.jp
+kanie.aichi.jp
+kariya.aichi.jp
+kasugai.aichi.jp
+kira.aichi.jp
+kiyosu.aichi.jp
+komaki.aichi.jp
+konan.aichi.jp
+kota.aichi.jp
+mihama.aichi.jp
+miyoshi.aichi.jp
+nagakute.aichi.jp
+nishio.aichi.jp
+nisshin.aichi.jp
+obu.aichi.jp
+oguchi.aichi.jp
+oharu.aichi.jp
+okazaki.aichi.jp
+owariasahi.aichi.jp
+seto.aichi.jp
+shikatsu.aichi.jp
+shinshiro.aichi.jp
+shitara.aichi.jp
+tahara.aichi.jp
+takahama.aichi.jp
+tobishima.aichi.jp
+toei.aichi.jp
+togo.aichi.jp
+tokai.aichi.jp
+tokoname.aichi.jp
+toyoake.aichi.jp
+toyohashi.aichi.jp
+toyokawa.aichi.jp
+toyone.aichi.jp
+toyota.aichi.jp
+tsushima.aichi.jp
+yatomi.aichi.jp
+akita.akita.jp
+daisen.akita.jp
+fujisato.akita.jp
+gojome.akita.jp
+hachirogata.akita.jp
+happou.akita.jp
+higashinaruse.akita.jp
+honjo.akita.jp
+honjyo.akita.jp
+ikawa.akita.jp
+kamikoani.akita.jp
+kamioka.akita.jp
+katagami.akita.jp
+kazuno.akita.jp
+kitaakita.akita.jp
+kosaka.akita.jp
+kyowa.akita.jp
+misato.akita.jp
+mitane.akita.jp
+moriyoshi.akita.jp
+nikaho.akita.jp
+noshiro.akita.jp
+odate.akita.jp
+oga.akita.jp
+ogata.akita.jp
+semboku.akita.jp
+yokote.akita.jp
+yurihonjo.akita.jp
+aomori.aomori.jp
+gonohe.aomori.jp
+hachinohe.aomori.jp
+hashikami.aomori.jp
+hiranai.aomori.jp
+hirosaki.aomori.jp
+itayanagi.aomori.jp
+kuroishi.aomori.jp
+misawa.aomori.jp
+mutsu.aomori.jp
+nakadomari.aomori.jp
+noheji.aomori.jp
+oirase.aomori.jp
+owani.aomori.jp
+rokunohe.aomori.jp
+sannohe.aomori.jp
+shichinohe.aomori.jp
+shingo.aomori.jp
+takko.aomori.jp
+towada.aomori.jp
+tsugaru.aomori.jp
+tsuruta.aomori.jp
+abiko.chiba.jp
+asahi.chiba.jp
+chonan.chiba.jp
+chosei.chiba.jp
+choshi.chiba.jp
+chuo.chiba.jp
+funabashi.chiba.jp
+futtsu.chiba.jp
+hanamigawa.chiba.jp
+ichihara.chiba.jp
+ichikawa.chiba.jp
+ichinomiya.chiba.jp
+inzai.chiba.jp
+isumi.chiba.jp
+kamagaya.chiba.jp
+kamogawa.chiba.jp
+kashiwa.chiba.jp
+katori.chiba.jp
+katsuura.chiba.jp
+kimitsu.chiba.jp
+kisarazu.chiba.jp
+kozaki.chiba.jp
+kujukuri.chiba.jp
+kyonan.chiba.jp
+matsudo.chiba.jp
+midori.chiba.jp
+mihama.chiba.jp
+minamiboso.chiba.jp
+mobara.chiba.jp
+mutsuzawa.chiba.jp
+nagara.chiba.jp
+nagareyama.chiba.jp
+narashino.chiba.jp
+narita.chiba.jp
+noda.chiba.jp
+oamishirasato.chiba.jp
+omigawa.chiba.jp
+onjuku.chiba.jp
+otaki.chiba.jp
+sakae.chiba.jp
+sakura.chiba.jp
+shimofusa.chiba.jp
+shirako.chiba.jp
+shiroi.chiba.jp
+shisui.chiba.jp
+sodegaura.chiba.jp
+sosa.chiba.jp
+tako.chiba.jp
+tateyama.chiba.jp
+togane.chiba.jp
+tohnosho.chiba.jp
+tomisato.chiba.jp
+urayasu.chiba.jp
+yachimata.chiba.jp
+yachiyo.chiba.jp
+yokaichiba.chiba.jp
+yokoshibahikari.chiba.jp
+yotsukaido.chiba.jp
+ainan.ehime.jp
+honai.ehime.jp
+ikata.ehime.jp
+imabari.ehime.jp
+iyo.ehime.jp
+kamijima.ehime.jp
+kihoku.ehime.jp
+kumakogen.ehime.jp
+masaki.ehime.jp
+matsuno.ehime.jp
+matsuyama.ehime.jp
+namikata.ehime.jp
+niihama.ehime.jp
+ozu.ehime.jp
+saijo.ehime.jp
+seiyo.ehime.jp
+shikokuchuo.ehime.jp
+tobe.ehime.jp
+toon.ehime.jp
+uchiko.ehime.jp
+uwajima.ehime.jp
+yawatahama.ehime.jp
+echizen.fukui.jp
+eiheiji.fukui.jp
+fukui.fukui.jp
+ikeda.fukui.jp
+katsuyama.fukui.jp
+mihama.fukui.jp
+minamiechizen.fukui.jp
+obama.fukui.jp
+ohi.fukui.jp
+ono.fukui.jp
+sabae.fukui.jp
+sakai.fukui.jp
+takahama.fukui.jp
+tsuruga.fukui.jp
+wakasa.fukui.jp
+ashiya.fukuoka.jp
+buzen.fukuoka.jp
+chikugo.fukuoka.jp
+chikuho.fukuoka.jp
+chikujo.fukuoka.jp
+chikushino.fukuoka.jp
+chikuzen.fukuoka.jp
+chuo.fukuoka.jp
+dazaifu.fukuoka.jp
+fukuchi.fukuoka.jp
+hakata.fukuoka.jp
+higashi.fukuoka.jp
+hirokawa.fukuoka.jp
+hisayama.fukuoka.jp
+iizuka.fukuoka.jp
+inatsuki.fukuoka.jp
+kaho.fukuoka.jp
+kasuga.fukuoka.jp
+kasuya.fukuoka.jp
+kawara.fukuoka.jp
+keisen.fukuoka.jp
+koga.fukuoka.jp
+kurate.fukuoka.jp
+kurogi.fukuoka.jp
+kurume.fukuoka.jp
+minami.fukuoka.jp
+miyako.fukuoka.jp
+miyama.fukuoka.jp
+miyawaka.fukuoka.jp
+mizumaki.fukuoka.jp
+munakata.fukuoka.jp
+nakagawa.fukuoka.jp
+nakama.fukuoka.jp
+nishi.fukuoka.jp
+nogata.fukuoka.jp
+ogori.fukuoka.jp
+okagaki.fukuoka.jp
+okawa.fukuoka.jp
+oki.fukuoka.jp
+omuta.fukuoka.jp
+onga.fukuoka.jp
+onojo.fukuoka.jp
+oto.fukuoka.jp
+saigawa.fukuoka.jp
+sasaguri.fukuoka.jp
+shingu.fukuoka.jp
+shinyoshitomi.fukuoka.jp
+shonai.fukuoka.jp
+soeda.fukuoka.jp
+sue.fukuoka.jp
+tachiarai.fukuoka.jp
+tagawa.fukuoka.jp
+takata.fukuoka.jp
+toho.fukuoka.jp
+toyotsu.fukuoka.jp
+tsuiki.fukuoka.jp
+ukiha.fukuoka.jp
+umi.fukuoka.jp
+usui.fukuoka.jp
+yamada.fukuoka.jp
+yame.fukuoka.jp
+yanagawa.fukuoka.jp
+yukuhashi.fukuoka.jp
+aizubange.fukushima.jp
+aizumisato.fukushima.jp
+aizuwakamatsu.fukushima.jp
+asakawa.fukushima.jp
+bandai.fukushima.jp
+date.fukushima.jp
+fukushima.fukushima.jp
+furudono.fukushima.jp
+futaba.fukushima.jp
+hanawa.fukushima.jp
+higashi.fukushima.jp
+hirata.fukushima.jp
+hirono.fukushima.jp
+iitate.fukushima.jp
+inawashiro.fukushima.jp
+ishikawa.fukushima.jp
+iwaki.fukushima.jp
+izumizaki.fukushima.jp
+kagamiishi.fukushima.jp
+kaneyama.fukushima.jp
+kawamata.fukushima.jp
+kitakata.fukushima.jp
+kitashiobara.fukushima.jp
+koori.fukushima.jp
+koriyama.fukushima.jp
+kunimi.fukushima.jp
+miharu.fukushima.jp
+mishima.fukushima.jp
+namie.fukushima.jp
+nango.fukushima.jp
+nishiaizu.fukushima.jp
+nishigo.fukushima.jp
+okuma.fukushima.jp
+omotego.fukushima.jp
+ono.fukushima.jp
+otama.fukushima.jp
+samegawa.fukushima.jp
+shimogo.fukushima.jp
+shirakawa.fukushima.jp
+showa.fukushima.jp
+soma.fukushima.jp
+sukagawa.fukushima.jp
+taishin.fukushima.jp
+tamakawa.fukushima.jp
+tanagura.fukushima.jp
+tenei.fukushima.jp
+yabuki.fukushima.jp
+yamato.fukushima.jp
+yamatsuri.fukushima.jp
+yanaizu.fukushima.jp
+yugawa.fukushima.jp
+anpachi.gifu.jp
+ena.gifu.jp
+gifu.gifu.jp
+ginan.gifu.jp
+godo.gifu.jp
+gujo.gifu.jp
+hashima.gifu.jp
+hichiso.gifu.jp
+hida.gifu.jp
+higashishirakawa.gifu.jp
+ibigawa.gifu.jp
+ikeda.gifu.jp
+kakamigahara.gifu.jp
+kani.gifu.jp
+kasahara.gifu.jp
+kasamatsu.gifu.jp
+kawaue.gifu.jp
+kitagata.gifu.jp
+mino.gifu.jp
+minokamo.gifu.jp
+mitake.gifu.jp
+mizunami.gifu.jp
+motosu.gifu.jp
+nakatsugawa.gifu.jp
+ogaki.gifu.jp
+sakahogi.gifu.jp
+seki.gifu.jp
+sekigahara.gifu.jp
+shirakawa.gifu.jp
+tajimi.gifu.jp
+takayama.gifu.jp
+tarui.gifu.jp
+toki.gifu.jp
+tomika.gifu.jp
+wanouchi.gifu.jp
+yamagata.gifu.jp
+yaotsu.gifu.jp
+yoro.gifu.jp
+annaka.gunma.jp
+chiyoda.gunma.jp
+fujioka.gunma.jp
+higashiagatsuma.gunma.jp
+isesaki.gunma.jp
+itakura.gunma.jp
+kanna.gunma.jp
+kanra.gunma.jp
+katashina.gunma.jp
+kawaba.gunma.jp
+kiryu.gunma.jp
+kusatsu.gunma.jp
+maebashi.gunma.jp
+meiwa.gunma.jp
+midori.gunma.jp
+minakami.gunma.jp
+naganohara.gunma.jp
+nakanojo.gunma.jp
+nanmoku.gunma.jp
+numata.gunma.jp
+oizumi.gunma.jp
+ora.gunma.jp
+ota.gunma.jp
+shibukawa.gunma.jp
+shimonita.gunma.jp
+shinto.gunma.jp
+showa.gunma.jp
+takasaki.gunma.jp
+takayama.gunma.jp
+tamamura.gunma.jp
+tatebayashi.gunma.jp
+tomioka.gunma.jp
+tsukiyono.gunma.jp
+tsumagoi.gunma.jp
+ueno.gunma.jp
+yoshioka.gunma.jp
+asaminami.hiroshima.jp
+daiwa.hiroshima.jp
+etajima.hiroshima.jp
+fuchu.hiroshima.jp
+fukuyama.hiroshima.jp
+hatsukaichi.hiroshima.jp
+higashihiroshima.hiroshima.jp
+hongo.hiroshima.jp
+jinsekikogen.hiroshima.jp
+kaita.hiroshima.jp
+kui.hiroshima.jp
+kumano.hiroshima.jp
+kure.hiroshima.jp
+mihara.hiroshima.jp
+miyoshi.hiroshima.jp
+naka.hiroshima.jp
+onomichi.hiroshima.jp
+osakikamijima.hiroshima.jp
+otake.hiroshima.jp
+saka.hiroshima.jp
+sera.hiroshima.jp
+seranishi.hiroshima.jp
+shinichi.hiroshima.jp
+shobara.hiroshima.jp
+takehara.hiroshima.jp
+abashiri.hokkaido.jp
+abira.hokkaido.jp
+aibetsu.hokkaido.jp
+akabira.hokkaido.jp
+akkeshi.hokkaido.jp
+asahikawa.hokkaido.jp
+ashibetsu.hokkaido.jp
+ashoro.hokkaido.jp
+assabu.hokkaido.jp
+atsuma.hokkaido.jp
+bibai.hokkaido.jp
+biei.hokkaido.jp
+bifuka.hokkaido.jp
+bihoro.hokkaido.jp
+biratori.hokkaido.jp
+chippubetsu.hokkaido.jp
+chitose.hokkaido.jp
+date.hokkaido.jp
+ebetsu.hokkaido.jp
+embetsu.hokkaido.jp
+eniwa.hokkaido.jp
+erimo.hokkaido.jp
+esan.hokkaido.jp
+esashi.hokkaido.jp
+fukagawa.hokkaido.jp
+fukushima.hokkaido.jp
+furano.hokkaido.jp
+furubira.hokkaido.jp
+haboro.hokkaido.jp
+hakodate.hokkaido.jp
+hamatonbetsu.hokkaido.jp
+hidaka.hokkaido.jp
+higashikagura.hokkaido.jp
+higashikawa.hokkaido.jp
+hiroo.hokkaido.jp
+hokuryu.hokkaido.jp
+hokuto.hokkaido.jp
+honbetsu.hokkaido.jp
+horokanai.hokkaido.jp
+horonobe.hokkaido.jp
+ikeda.hokkaido.jp
+imakane.hokkaido.jp
+ishikari.hokkaido.jp
+iwamizawa.hokkaido.jp
+iwanai.hokkaido.jp
+kamifurano.hokkaido.jp
+kamikawa.hokkaido.jp
+kamishihoro.hokkaido.jp
+kamisunagawa.hokkaido.jp
+kamoenai.hokkaido.jp
+kayabe.hokkaido.jp
+kembuchi.hokkaido.jp
+kikonai.hokkaido.jp
+kimobetsu.hokkaido.jp
+kitahiroshima.hokkaido.jp
+kitami.hokkaido.jp
+kiyosato.hokkaido.jp
+koshimizu.hokkaido.jp
+kunneppu.hokkaido.jp
+kuriyama.hokkaido.jp
+kuromatsunai.hokkaido.jp
+kushiro.hokkaido.jp
+kutchan.hokkaido.jp
+kyowa.hokkaido.jp
+mashike.hokkaido.jp
+matsumae.hokkaido.jp
+mikasa.hokkaido.jp
+minamifurano.hokkaido.jp
+mombetsu.hokkaido.jp
+moseushi.hokkaido.jp
+mukawa.hokkaido.jp
+muroran.hokkaido.jp
+naie.hokkaido.jp
+nakagawa.hokkaido.jp
+nakasatsunai.hokkaido.jp
+nakatombetsu.hokkaido.jp
+nanae.hokkaido.jp
+nanporo.hokkaido.jp
+nayoro.hokkaido.jp
+nemuro.hokkaido.jp
+niikappu.hokkaido.jp
+niki.hokkaido.jp
+nishiokoppe.hokkaido.jp
+noboribetsu.hokkaido.jp
+numata.hokkaido.jp
+obihiro.hokkaido.jp
+obira.hokkaido.jp
+oketo.hokkaido.jp
+okoppe.hokkaido.jp
+otaru.hokkaido.jp
+otobe.hokkaido.jp
+otofuke.hokkaido.jp
+otoineppu.hokkaido.jp
+oumu.hokkaido.jp
+ozora.hokkaido.jp
+pippu.hokkaido.jp
+rankoshi.hokkaido.jp
+rebun.hokkaido.jp
+rikubetsu.hokkaido.jp
+rishiri.hokkaido.jp
+rishirifuji.hokkaido.jp
+saroma.hokkaido.jp
+sarufutsu.hokkaido.jp
+shakotan.hokkaido.jp
+shari.hokkaido.jp
+shibecha.hokkaido.jp
+shibetsu.hokkaido.jp
+shikabe.hokkaido.jp
+shikaoi.hokkaido.jp
+shimamaki.hokkaido.jp
+shimizu.hokkaido.jp
+shimokawa.hokkaido.jp
+shinshinotsu.hokkaido.jp
+shintoku.hokkaido.jp
+shiranuka.hokkaido.jp
+shiraoi.hokkaido.jp
+shiriuchi.hokkaido.jp
+sobetsu.hokkaido.jp
+sunagawa.hokkaido.jp
+taiki.hokkaido.jp
+takasu.hokkaido.jp
+takikawa.hokkaido.jp
+takinoue.hokkaido.jp
+teshikaga.hokkaido.jp
+tobetsu.hokkaido.jp
+tohma.hokkaido.jp
+tomakomai.hokkaido.jp
+tomari.hokkaido.jp
+toya.hokkaido.jp
+toyako.hokkaido.jp
+toyotomi.hokkaido.jp
+toyoura.hokkaido.jp
+tsubetsu.hokkaido.jp
+tsukigata.hokkaido.jp
+urakawa.hokkaido.jp
+urausu.hokkaido.jp
+uryu.hokkaido.jp
+utashinai.hokkaido.jp
+wakkanai.hokkaido.jp
+wassamu.hokkaido.jp
+yakumo.hokkaido.jp
+yoichi.hokkaido.jp
+aioi.hyogo.jp
+akashi.hyogo.jp
+ako.hyogo.jp
+amagasaki.hyogo.jp
+aogaki.hyogo.jp
+asago.hyogo.jp
+ashiya.hyogo.jp
+awaji.hyogo.jp
+fukusaki.hyogo.jp
+goshiki.hyogo.jp
+harima.hyogo.jp
+himeji.hyogo.jp
+ichikawa.hyogo.jp
+inagawa.hyogo.jp
+itami.hyogo.jp
+kakogawa.hyogo.jp
+kamigori.hyogo.jp
+kamikawa.hyogo.jp
+kasai.hyogo.jp
+kasuga.hyogo.jp
+kawanishi.hyogo.jp
+miki.hyogo.jp
+minamiawaji.hyogo.jp
+nishinomiya.hyogo.jp
+nishiwaki.hyogo.jp
+ono.hyogo.jp
+sanda.hyogo.jp
+sannan.hyogo.jp
+sasayama.hyogo.jp
+sayo.hyogo.jp
+shingu.hyogo.jp
+shinonsen.hyogo.jp
+shiso.hyogo.jp
+sumoto.hyogo.jp
+taishi.hyogo.jp
+taka.hyogo.jp
+takarazuka.hyogo.jp
+takasago.hyogo.jp
+takino.hyogo.jp
+tamba.hyogo.jp
+tatsuno.hyogo.jp
+toyooka.hyogo.jp
+yabu.hyogo.jp
+yashiro.hyogo.jp
+yoka.hyogo.jp
+yokawa.hyogo.jp
+ami.ibaraki.jp
+asahi.ibaraki.jp
+bando.ibaraki.jp
+chikusei.ibaraki.jp
+daigo.ibaraki.jp
+fujishiro.ibaraki.jp
+hitachi.ibaraki.jp
+hitachinaka.ibaraki.jp
+hitachiomiya.ibaraki.jp
+hitachiota.ibaraki.jp
+ibaraki.ibaraki.jp
+ina.ibaraki.jp
+inashiki.ibaraki.jp
+itako.ibaraki.jp
+iwama.ibaraki.jp
+joso.ibaraki.jp
+kamisu.ibaraki.jp
+kasama.ibaraki.jp
+kashima.ibaraki.jp
+kasumigaura.ibaraki.jp
+koga.ibaraki.jp
+miho.ibaraki.jp
+mito.ibaraki.jp
+moriya.ibaraki.jp
+naka.ibaraki.jp
+namegata.ibaraki.jp
+oarai.ibaraki.jp
+ogawa.ibaraki.jp
+omitama.ibaraki.jp
+ryugasaki.ibaraki.jp
+sakai.ibaraki.jp
+sakuragawa.ibaraki.jp
+shimodate.ibaraki.jp
+shimotsuma.ibaraki.jp
+shirosato.ibaraki.jp
+sowa.ibaraki.jp
+suifu.ibaraki.jp
+takahagi.ibaraki.jp
+tamatsukuri.ibaraki.jp
+tokai.ibaraki.jp
+tomobe.ibaraki.jp
+tone.ibaraki.jp
+toride.ibaraki.jp
+tsuchiura.ibaraki.jp
+tsukuba.ibaraki.jp
+uchihara.ibaraki.jp
+ushiku.ibaraki.jp
+yachiyo.ibaraki.jp
+yamagata.ibaraki.jp
+yawara.ibaraki.jp
+yuki.ibaraki.jp
+anamizu.ishikawa.jp
+hakui.ishikawa.jp
+hakusan.ishikawa.jp
+kaga.ishikawa.jp
+kahoku.ishikawa.jp
+kanazawa.ishikawa.jp
+kawakita.ishikawa.jp
+komatsu.ishikawa.jp
+nakanoto.ishikawa.jp
+nanao.ishikawa.jp
+nomi.ishikawa.jp
+nonoichi.ishikawa.jp
+noto.ishikawa.jp
+shika.ishikawa.jp
+suzu.ishikawa.jp
+tsubata.ishikawa.jp
+tsurugi.ishikawa.jp
+uchinada.ishikawa.jp
+wajima.ishikawa.jp
+fudai.iwate.jp
+fujisawa.iwate.jp
+hanamaki.iwate.jp
+hiraizumi.iwate.jp
+hirono.iwate.jp
+ichinohe.iwate.jp
+ichinoseki.iwate.jp
+iwaizumi.iwate.jp
+iwate.iwate.jp
+joboji.iwate.jp
+kamaishi.iwate.jp
+kanegasaki.iwate.jp
+karumai.iwate.jp
+kawai.iwate.jp
+kitakami.iwate.jp
+kuji.iwate.jp
+kunohe.iwate.jp
+kuzumaki.iwate.jp
+miyako.iwate.jp
+mizusawa.iwate.jp
+morioka.iwate.jp
+ninohe.iwate.jp
+noda.iwate.jp
+ofunato.iwate.jp
+oshu.iwate.jp
+otsuchi.iwate.jp
+rikuzentakata.iwate.jp
+shiwa.iwate.jp
+shizukuishi.iwate.jp
+sumita.iwate.jp
+takizawa.iwate.jp
+tanohata.iwate.jp
+tono.iwate.jp
+yahaba.iwate.jp
+yamada.iwate.jp
+ayagawa.kagawa.jp
+higashikagawa.kagawa.jp
+kanonji.kagawa.jp
+kotohira.kagawa.jp
+manno.kagawa.jp
+marugame.kagawa.jp
+mitoyo.kagawa.jp
+naoshima.kagawa.jp
+sanuki.kagawa.jp
+tadotsu.kagawa.jp
+takamatsu.kagawa.jp
+tonosho.kagawa.jp
+uchinomi.kagawa.jp
+utazu.kagawa.jp
+zentsuji.kagawa.jp
+akune.kagoshima.jp
+amami.kagoshima.jp
+hioki.kagoshima.jp
+isa.kagoshima.jp
+isen.kagoshima.jp
+izumi.kagoshima.jp
+kagoshima.kagoshima.jp
+kanoya.kagoshima.jp
+kawanabe.kagoshima.jp
+kinko.kagoshima.jp
+kouyama.kagoshima.jp
+makurazaki.kagoshima.jp
+matsumoto.kagoshima.jp
+minamitane.kagoshima.jp
+nakatane.kagoshima.jp
+nishinoomote.kagoshima.jp
+satsumasendai.kagoshima.jp
+soo.kagoshima.jp
+tarumizu.kagoshima.jp
+yusui.kagoshima.jp
+aikawa.kanagawa.jp
+atsugi.kanagawa.jp
+ayase.kanagawa.jp
+chigasaki.kanagawa.jp
+ebina.kanagawa.jp
+fujisawa.kanagawa.jp
+hadano.kanagawa.jp
+hakone.kanagawa.jp
+hiratsuka.kanagawa.jp
+isehara.kanagawa.jp
+kaisei.kanagawa.jp
+kamakura.kanagawa.jp
+kiyokawa.kanagawa.jp
+matsuda.kanagawa.jp
+minamiashigara.kanagawa.jp
+miura.kanagawa.jp
+nakai.kanagawa.jp
+ninomiya.kanagawa.jp
+odawara.kanagawa.jp
+oi.kanagawa.jp
+oiso.kanagawa.jp
+sagamihara.kanagawa.jp
+samukawa.kanagawa.jp
+tsukui.kanagawa.jp
+yamakita.kanagawa.jp
+yamato.kanagawa.jp
+yokosuka.kanagawa.jp
+yugawara.kanagawa.jp
+zama.kanagawa.jp
+zushi.kanagawa.jp
+aki.kochi.jp
+geisei.kochi.jp
+hidaka.kochi.jp
+higashitsuno.kochi.jp
+ino.kochi.jp
+kagami.kochi.jp
+kami.kochi.jp
+kitagawa.kochi.jp
+kochi.kochi.jp
+mihara.kochi.jp
+motoyama.kochi.jp
+muroto.kochi.jp
+nahari.kochi.jp
+nakamura.kochi.jp
+nankoku.kochi.jp
+nishitosa.kochi.jp
+niyodogawa.kochi.jp
+ochi.kochi.jp
+okawa.kochi.jp
+otoyo.kochi.jp
+otsuki.kochi.jp
+sakawa.kochi.jp
+sukumo.kochi.jp
+susaki.kochi.jp
+tosa.kochi.jp
+tosashimizu.kochi.jp
+toyo.kochi.jp
+tsuno.kochi.jp
+umaji.kochi.jp
+yasuda.kochi.jp
+yusuhara.kochi.jp
+amakusa.kumamoto.jp
+arao.kumamoto.jp
+aso.kumamoto.jp
+choyo.kumamoto.jp
+gyokuto.kumamoto.jp
+hitoyoshi.kumamoto.jp
+kamiamakusa.kumamoto.jp
+kashima.kumamoto.jp
+kikuchi.kumamoto.jp
+kosa.kumamoto.jp
+kumamoto.kumamoto.jp
+mashiki.kumamoto.jp
+mifune.kumamoto.jp
+minamata.kumamoto.jp
+minamioguni.kumamoto.jp
+nagasu.kumamoto.jp
+nishihara.kumamoto.jp
+oguni.kumamoto.jp
+ozu.kumamoto.jp
+sumoto.kumamoto.jp
+takamori.kumamoto.jp
+uki.kumamoto.jp
+uto.kumamoto.jp
+yamaga.kumamoto.jp
+yamato.kumamoto.jp
+yatsushiro.kumamoto.jp
+ayabe.kyoto.jp
+fukuchiyama.kyoto.jp
+higashiyama.kyoto.jp
+ide.kyoto.jp
+ine.kyoto.jp
+joyo.kyoto.jp
+kameoka.kyoto.jp
+kamo.kyoto.jp
+kita.kyoto.jp
+kizu.kyoto.jp
+kumiyama.kyoto.jp
+kyotamba.kyoto.jp
+kyotanabe.kyoto.jp
+kyotango.kyoto.jp
+maizuru.kyoto.jp
+minami.kyoto.jp
+minamiyamashiro.kyoto.jp
+miyazu.kyoto.jp
+muko.kyoto.jp
+nagaokakyo.kyoto.jp
+nakagyo.kyoto.jp
+nantan.kyoto.jp
+oyamazaki.kyoto.jp
+sakyo.kyoto.jp
+seika.kyoto.jp
+tanabe.kyoto.jp
+uji.kyoto.jp
+ujitawara.kyoto.jp
+wazuka.kyoto.jp
+yamashina.kyoto.jp
+yawata.kyoto.jp
+asahi.mie.jp
+inabe.mie.jp
+ise.mie.jp
+kameyama.mie.jp
+kawagoe.mie.jp
+kiho.mie.jp
+kisosaki.mie.jp
+kiwa.mie.jp
+komono.mie.jp
+kumano.mie.jp
+kuwana.mie.jp
+matsusaka.mie.jp
+meiwa.mie.jp
+mihama.mie.jp
+minamiise.mie.jp
+misugi.mie.jp
+miyama.mie.jp
+nabari.mie.jp
+shima.mie.jp
+suzuka.mie.jp
+tado.mie.jp
+taiki.mie.jp
+taki.mie.jp
+tamaki.mie.jp
+toba.mie.jp
+tsu.mie.jp
+udono.mie.jp
+ureshino.mie.jp
+watarai.mie.jp
+yokkaichi.mie.jp
+furukawa.miyagi.jp
+higashimatsushima.miyagi.jp
+ishinomaki.miyagi.jp
+iwanuma.miyagi.jp
+kakuda.miyagi.jp
+kami.miyagi.jp
+kawasaki.miyagi.jp
+kesennuma.miyagi.jp
+marumori.miyagi.jp
+matsushima.miyagi.jp
+minamisanriku.miyagi.jp
+misato.miyagi.jp
+murata.miyagi.jp
+natori.miyagi.jp
+ogawara.miyagi.jp
+ohira.miyagi.jp
+onagawa.miyagi.jp
+osaki.miyagi.jp
+rifu.miyagi.jp
+semine.miyagi.jp
+shibata.miyagi.jp
+shichikashuku.miyagi.jp
+shikama.miyagi.jp
+shiogama.miyagi.jp
+shiroishi.miyagi.jp
+tagajo.miyagi.jp
+taiwa.miyagi.jp
+tome.miyagi.jp
+tomiya.miyagi.jp
+wakuya.miyagi.jp
+watari.miyagi.jp
+yamamoto.miyagi.jp
+zao.miyagi.jp
+aya.miyazaki.jp
+ebino.miyazaki.jp
+gokase.miyazaki.jp
+hyuga.miyazaki.jp
+kadogawa.miyazaki.jp
+kawaminami.miyazaki.jp
+kijo.miyazaki.jp
+kitagawa.miyazaki.jp
+kitakata.miyazaki.jp
+kitaura.miyazaki.jp
+kobayashi.miyazaki.jp
+kunitomi.miyazaki.jp
+kushima.miyazaki.jp
+mimata.miyazaki.jp
+miyakonojo.miyazaki.jp
+miyazaki.miyazaki.jp
+morotsuka.miyazaki.jp
+nichinan.miyazaki.jp
+nishimera.miyazaki.jp
+nobeoka.miyazaki.jp
+saito.miyazaki.jp
+shiiba.miyazaki.jp
+shintomi.miyazaki.jp
+takaharu.miyazaki.jp
+takanabe.miyazaki.jp
+takazaki.miyazaki.jp
+tsuno.miyazaki.jp
+achi.nagano.jp
+agematsu.nagano.jp
+anan.nagano.jp
+aoki.nagano.jp
+asahi.nagano.jp
+azumino.nagano.jp
+chikuhoku.nagano.jp
+chikuma.nagano.jp
+chino.nagano.jp
+fujimi.nagano.jp
+hakuba.nagano.jp
+hara.nagano.jp
+hiraya.nagano.jp
+iida.nagano.jp
+iijima.nagano.jp
+iiyama.nagano.jp
+iizuna.nagano.jp
+ikeda.nagano.jp
+ikusaka.nagano.jp
+ina.nagano.jp
+karuizawa.nagano.jp
+kawakami.nagano.jp
+kiso.nagano.jp
+kisofukushima.nagano.jp
+kitaaiki.nagano.jp
+komagane.nagano.jp
+komoro.nagano.jp
+matsukawa.nagano.jp
+matsumoto.nagano.jp
+miasa.nagano.jp
+minamiaiki.nagano.jp
+minamimaki.nagano.jp
+minamiminowa.nagano.jp
+minowa.nagano.jp
+miyada.nagano.jp
+miyota.nagano.jp
+mochizuki.nagano.jp
+nagano.nagano.jp
+nagawa.nagano.jp
+nagiso.nagano.jp
+nakagawa.nagano.jp
+nakano.nagano.jp
+nozawaonsen.nagano.jp
+obuse.nagano.jp
+ogawa.nagano.jp
+okaya.nagano.jp
+omachi.nagano.jp
+omi.nagano.jp
+ookuwa.nagano.jp
+ooshika.nagano.jp
+otaki.nagano.jp
+otari.nagano.jp
+sakae.nagano.jp
+sakaki.nagano.jp
+saku.nagano.jp
+sakuho.nagano.jp
+shimosuwa.nagano.jp
+shinanomachi.nagano.jp
+shiojiri.nagano.jp
+suwa.nagano.jp
+suzaka.nagano.jp
+takagi.nagano.jp
+takamori.nagano.jp
+takayama.nagano.jp
+tateshina.nagano.jp
+tatsuno.nagano.jp
+togakushi.nagano.jp
+togura.nagano.jp
+tomi.nagano.jp
+ueda.nagano.jp
+wada.nagano.jp
+yamagata.nagano.jp
+yamanouchi.nagano.jp
+yasaka.nagano.jp
+yasuoka.nagano.jp
+chijiwa.nagasaki.jp
+futsu.nagasaki.jp
+goto.nagasaki.jp
+hasami.nagasaki.jp
+hirado.nagasaki.jp
+iki.nagasaki.jp
+isahaya.nagasaki.jp
+kawatana.nagasaki.jp
+kuchinotsu.nagasaki.jp
+matsuura.nagasaki.jp
+nagasaki.nagasaki.jp
+obama.nagasaki.jp
+omura.nagasaki.jp
+oseto.nagasaki.jp
+saikai.nagasaki.jp
+sasebo.nagasaki.jp
+seihi.nagasaki.jp
+shimabara.nagasaki.jp
+shinkamigoto.nagasaki.jp
+togitsu.nagasaki.jp
+tsushima.nagasaki.jp
+unzen.nagasaki.jp
+ando.nara.jp
+gose.nara.jp
+heguri.nara.jp
+higashiyoshino.nara.jp
+ikaruga.nara.jp
+ikoma.nara.jp
+kamikitayama.nara.jp
+kanmaki.nara.jp
+kashiba.nara.jp
+kashihara.nara.jp
+katsuragi.nara.jp
+kawai.nara.jp
+kawakami.nara.jp
+kawanishi.nara.jp
+koryo.nara.jp
+kurotaki.nara.jp
+mitsue.nara.jp
+miyake.nara.jp
+nara.nara.jp
+nosegawa.nara.jp
+oji.nara.jp
+ouda.nara.jp
+oyodo.nara.jp
+sakurai.nara.jp
+sango.nara.jp
+shimoichi.nara.jp
+shimokitayama.nara.jp
+shinjo.nara.jp
+soni.nara.jp
+takatori.nara.jp
+tawaramoto.nara.jp
+tenkawa.nara.jp
+tenri.nara.jp
+uda.nara.jp
+yamatokoriyama.nara.jp
+yamatotakada.nara.jp
+yamazoe.nara.jp
+yoshino.nara.jp
+aga.niigata.jp
+agano.niigata.jp
+gosen.niigata.jp
+itoigawa.niigata.jp
+izumozaki.niigata.jp
+joetsu.niigata.jp
+kamo.niigata.jp
+kariwa.niigata.jp
+kashiwazaki.niigata.jp
+minamiuonuma.niigata.jp
+mitsuke.niigata.jp
+muika.niigata.jp
+murakami.niigata.jp
+myoko.niigata.jp
+nagaoka.niigata.jp
+niigata.niigata.jp
+ojiya.niigata.jp
+omi.niigata.jp
+sado.niigata.jp
+sanjo.niigata.jp
+seiro.niigata.jp
+seirou.niigata.jp
+sekikawa.niigata.jp
+shibata.niigata.jp
+tagami.niigata.jp
+tainai.niigata.jp
+tochio.niigata.jp
+tokamachi.niigata.jp
+tsubame.niigata.jp
+tsunan.niigata.jp
+uonuma.niigata.jp
+yahiko.niigata.jp
+yoita.niigata.jp
+yuzawa.niigata.jp
+beppu.oita.jp
+bungoono.oita.jp
+bungotakada.oita.jp
+hasama.oita.jp
+hiji.oita.jp
+himeshima.oita.jp
+hita.oita.jp
+kamitsue.oita.jp
+kokonoe.oita.jp
+kuju.oita.jp
+kunisaki.oita.jp
+kusu.oita.jp
+oita.oita.jp
+saiki.oita.jp
+taketa.oita.jp
+tsukumi.oita.jp
+usa.oita.jp
+usuki.oita.jp
+yufu.oita.jp
+akaiwa.okayama.jp
+asakuchi.okayama.jp
+bizen.okayama.jp
+hayashima.okayama.jp
+ibara.okayama.jp
+kagamino.okayama.jp
+kasaoka.okayama.jp
+kibichuo.okayama.jp
+kumenan.okayama.jp
+kurashiki.okayama.jp
+maniwa.okayama.jp
+misaki.okayama.jp
+nagi.okayama.jp
+niimi.okayama.jp
+nishiawakura.okayama.jp
+okayama.okayama.jp
+satosho.okayama.jp
+setouchi.okayama.jp
+shinjo.okayama.jp
+shoo.okayama.jp
+soja.okayama.jp
+takahashi.okayama.jp
+tamano.okayama.jp
+tsuyama.okayama.jp
+wake.okayama.jp
+yakage.okayama.jp
+aguni.okinawa.jp
+ginowan.okinawa.jp
+ginoza.okinawa.jp
+gushikami.okinawa.jp
+haebaru.okinawa.jp
+higashi.okinawa.jp
+hirara.okinawa.jp
+iheya.okinawa.jp
+ishigaki.okinawa.jp
+ishikawa.okinawa.jp
+itoman.okinawa.jp
+izena.okinawa.jp
+kadena.okinawa.jp
+kin.okinawa.jp
+kitadaito.okinawa.jp
+kitanakagusuku.okinawa.jp
+kumejima.okinawa.jp
+kunigami.okinawa.jp
+minamidaito.okinawa.jp
+motobu.okinawa.jp
+nago.okinawa.jp
+naha.okinawa.jp
+nakagusuku.okinawa.jp
+nakijin.okinawa.jp
+nanjo.okinawa.jp
+nishihara.okinawa.jp
+ogimi.okinawa.jp
+okinawa.okinawa.jp
+onna.okinawa.jp
+shimoji.okinawa.jp
+taketomi.okinawa.jp
+tarama.okinawa.jp
+tokashiki.okinawa.jp
+tomigusuku.okinawa.jp
+tonaki.okinawa.jp
+urasoe.okinawa.jp
+uruma.okinawa.jp
+yaese.okinawa.jp
+yomitan.okinawa.jp
+yonabaru.okinawa.jp
+yonaguni.okinawa.jp
+zamami.okinawa.jp
+abeno.osaka.jp
+chihayaakasaka.osaka.jp
+chuo.osaka.jp
+daito.osaka.jp
+fujiidera.osaka.jp
+habikino.osaka.jp
+hannan.osaka.jp
+higashiosaka.osaka.jp
+higashisumiyoshi.osaka.jp
+higashiyodogawa.osaka.jp
+hirakata.osaka.jp
+ibaraki.osaka.jp
+ikeda.osaka.jp
+izumi.osaka.jp
+izumiotsu.osaka.jp
+izumisano.osaka.jp
+kadoma.osaka.jp
+kaizuka.osaka.jp
+kanan.osaka.jp
+kashiwara.osaka.jp
+katano.osaka.jp
+kawachinagano.osaka.jp
+kishiwada.osaka.jp
+kita.osaka.jp
+kumatori.osaka.jp
+matsubara.osaka.jp
+minato.osaka.jp
+minoh.osaka.jp
+misaki.osaka.jp
+moriguchi.osaka.jp
+neyagawa.osaka.jp
+nishi.osaka.jp
+nose.osaka.jp
+osakasayama.osaka.jp
+sakai.osaka.jp
+sayama.osaka.jp
+sennan.osaka.jp
+settsu.osaka.jp
+shijonawate.osaka.jp
+shimamoto.osaka.jp
+suita.osaka.jp
+tadaoka.osaka.jp
+taishi.osaka.jp
+tajiri.osaka.jp
+takaishi.osaka.jp
+takatsuki.osaka.jp
+tondabayashi.osaka.jp
+toyonaka.osaka.jp
+toyono.osaka.jp
+yao.osaka.jp
+ariake.saga.jp
+arita.saga.jp
+fukudomi.saga.jp
+genkai.saga.jp
+hamatama.saga.jp
+hizen.saga.jp
+imari.saga.jp
+kamimine.saga.jp
+kanzaki.saga.jp
+karatsu.saga.jp
+kashima.saga.jp
+kitagata.saga.jp
+kitahata.saga.jp
+kiyama.saga.jp
+kouhoku.saga.jp
+kyuragi.saga.jp
+nishiarita.saga.jp
+ogi.saga.jp
+omachi.saga.jp
+ouchi.saga.jp
+saga.saga.jp
+shiroishi.saga.jp
+taku.saga.jp
+tara.saga.jp
+tosu.saga.jp
+yoshinogari.saga.jp
+arakawa.saitama.jp
+asaka.saitama.jp
+chichibu.saitama.jp
+fujimi.saitama.jp
+fujimino.saitama.jp
+fukaya.saitama.jp
+hanno.saitama.jp
+hanyu.saitama.jp
+hasuda.saitama.jp
+hatogaya.saitama.jp
+hatoyama.saitama.jp
+hidaka.saitama.jp
+higashichichibu.saitama.jp
+higashimatsuyama.saitama.jp
+honjo.saitama.jp
+ina.saitama.jp
+iruma.saitama.jp
+iwatsuki.saitama.jp
+kamiizumi.saitama.jp
+kamikawa.saitama.jp
+kamisato.saitama.jp
+kasukabe.saitama.jp
+kawagoe.saitama.jp
+kawaguchi.saitama.jp
+kawajima.saitama.jp
+kazo.saitama.jp
+kitamoto.saitama.jp
+koshigaya.saitama.jp
+kounosu.saitama.jp
+kuki.saitama.jp
+kumagaya.saitama.jp
+matsubushi.saitama.jp
+minano.saitama.jp
+misato.saitama.jp
+miyashiro.saitama.jp
+miyoshi.saitama.jp
+moroyama.saitama.jp
+nagatoro.saitama.jp
+namegawa.saitama.jp
+niiza.saitama.jp
+ogano.saitama.jp
+ogawa.saitama.jp
+ogose.saitama.jp
+okegawa.saitama.jp
+omiya.saitama.jp
+otaki.saitama.jp
+ranzan.saitama.jp
+ryokami.saitama.jp
+saitama.saitama.jp
+sakado.saitama.jp
+satte.saitama.jp
+sayama.saitama.jp
+shiki.saitama.jp
+shiraoka.saitama.jp
+soka.saitama.jp
+sugito.saitama.jp
+toda.saitama.jp
+tokigawa.saitama.jp
+tokorozawa.saitama.jp
+tsurugashima.saitama.jp
+urawa.saitama.jp
+warabi.saitama.jp
+yashio.saitama.jp
+yokoze.saitama.jp
+yono.saitama.jp
+yorii.saitama.jp
+yoshida.saitama.jp
+yoshikawa.saitama.jp
+yoshimi.saitama.jp
+aisho.shiga.jp
+gamo.shiga.jp
+higashiomi.shiga.jp
+hikone.shiga.jp
+koka.shiga.jp
+konan.shiga.jp
+kosei.shiga.jp
+koto.shiga.jp
+kusatsu.shiga.jp
+maibara.shiga.jp
+moriyama.shiga.jp
+nagahama.shiga.jp
+nishiazai.shiga.jp
+notogawa.shiga.jp
+omihachiman.shiga.jp
+otsu.shiga.jp
+ritto.shiga.jp
+ryuoh.shiga.jp
+takashima.shiga.jp
+takatsuki.shiga.jp
+torahime.shiga.jp
+toyosato.shiga.jp
+yasu.shiga.jp
+akagi.shimane.jp
+ama.shimane.jp
+gotsu.shimane.jp
+hamada.shimane.jp
+higashiizumo.shimane.jp
+hikawa.shimane.jp
+hikimi.shimane.jp
+izumo.shimane.jp
+kakinoki.shimane.jp
+masuda.shimane.jp
+matsue.shimane.jp
+misato.shimane.jp
+nishinoshima.shimane.jp
+ohda.shimane.jp
+okinoshima.shimane.jp
+okuizumo.shimane.jp
+shimane.shimane.jp
+tamayu.shimane.jp
+tsuwano.shimane.jp
+unnan.shimane.jp
+yakumo.shimane.jp
+yasugi.shimane.jp
+yatsuka.shimane.jp
+arai.shizuoka.jp
+atami.shizuoka.jp
+fuji.shizuoka.jp
+fujieda.shizuoka.jp
+fujikawa.shizuoka.jp
+fujinomiya.shizuoka.jp
+fukuroi.shizuoka.jp
+gotemba.shizuoka.jp
+haibara.shizuoka.jp
+hamamatsu.shizuoka.jp
+higashiizu.shizuoka.jp
+ito.shizuoka.jp
+iwata.shizuoka.jp
+izu.shizuoka.jp
+izunokuni.shizuoka.jp
+kakegawa.shizuoka.jp
+kannami.shizuoka.jp
+kawanehon.shizuoka.jp
+kawazu.shizuoka.jp
+kikugawa.shizuoka.jp
+kosai.shizuoka.jp
+makinohara.shizuoka.jp
+matsuzaki.shizuoka.jp
+minamiizu.shizuoka.jp
+mishima.shizuoka.jp
+morimachi.shizuoka.jp
+nishiizu.shizuoka.jp
+numazu.shizuoka.jp
+omaezaki.shizuoka.jp
+shimada.shizuoka.jp
+shimizu.shizuoka.jp
+shimoda.shizuoka.jp
+shizuoka.shizuoka.jp
+susono.shizuoka.jp
+yaizu.shizuoka.jp
+yoshida.shizuoka.jp
+ashikaga.tochigi.jp
+bato.tochigi.jp
+haga.tochigi.jp
+ichikai.tochigi.jp
+iwafune.tochigi.jp
+kaminokawa.tochigi.jp
+kanuma.tochigi.jp
+karasuyama.tochigi.jp
+kuroiso.tochigi.jp
+mashiko.tochigi.jp
+mibu.tochigi.jp
+moka.tochigi.jp
+motegi.tochigi.jp
+nasu.tochigi.jp
+nasushiobara.tochigi.jp
+nikko.tochigi.jp
+nishikata.tochigi.jp
+nogi.tochigi.jp
+ohira.tochigi.jp
+ohtawara.tochigi.jp
+oyama.tochigi.jp
+sakura.tochigi.jp
+sano.tochigi.jp
+shimotsuke.tochigi.jp
+shioya.tochigi.jp
+takanezawa.tochigi.jp
+tochigi.tochigi.jp
+tsuga.tochigi.jp
+ujiie.tochigi.jp
+utsunomiya.tochigi.jp
+yaita.tochigi.jp
+aizumi.tokushima.jp
+anan.tokushima.jp
+ichiba.tokushima.jp
+itano.tokushima.jp
+kainan.tokushima.jp
+komatsushima.tokushima.jp
+matsushige.tokushima.jp
+mima.tokushima.jp
+minami.tokushima.jp
+miyoshi.tokushima.jp
+mugi.tokushima.jp
+nakagawa.tokushima.jp
+naruto.tokushima.jp
+sanagochi.tokushima.jp
+shishikui.tokushima.jp
+tokushima.tokushima.jp
+wajiki.tokushima.jp
+adachi.tokyo.jp
+akiruno.tokyo.jp
+akishima.tokyo.jp
+aogashima.tokyo.jp
+arakawa.tokyo.jp
+bunkyo.tokyo.jp
+chiyoda.tokyo.jp
+chofu.tokyo.jp
+chuo.tokyo.jp
+edogawa.tokyo.jp
+fuchu.tokyo.jp
+fussa.tokyo.jp
+hachijo.tokyo.jp
+hachioji.tokyo.jp
+hamura.tokyo.jp
+higashikurume.tokyo.jp
+higashimurayama.tokyo.jp
+higashiyamato.tokyo.jp
+hino.tokyo.jp
+hinode.tokyo.jp
+hinohara.tokyo.jp
+inagi.tokyo.jp
+itabashi.tokyo.jp
+katsushika.tokyo.jp
+kita.tokyo.jp
+kiyose.tokyo.jp
+kodaira.tokyo.jp
+koganei.tokyo.jp
+kokubunji.tokyo.jp
+komae.tokyo.jp
+koto.tokyo.jp
+kouzushima.tokyo.jp
+kunitachi.tokyo.jp
+machida.tokyo.jp
+meguro.tokyo.jp
+minato.tokyo.jp
+mitaka.tokyo.jp
+mizuho.tokyo.jp
+musashimurayama.tokyo.jp
+musashino.tokyo.jp
+nakano.tokyo.jp
+nerima.tokyo.jp
+ogasawara.tokyo.jp
+okutama.tokyo.jp
+ome.tokyo.jp
+oshima.tokyo.jp
+ota.tokyo.jp
+setagaya.tokyo.jp
+shibuya.tokyo.jp
+shinagawa.tokyo.jp
+shinjuku.tokyo.jp
+suginami.tokyo.jp
+sumida.tokyo.jp
+tachikawa.tokyo.jp
+taito.tokyo.jp
+tama.tokyo.jp
+toshima.tokyo.jp
+chizu.tottori.jp
+hino.tottori.jp
+kawahara.tottori.jp
+koge.tottori.jp
+kotoura.tottori.jp
+misasa.tottori.jp
+nanbu.tottori.jp
+nichinan.tottori.jp
+sakaiminato.tottori.jp
+tottori.tottori.jp
+wakasa.tottori.jp
+yazu.tottori.jp
+yonago.tottori.jp
+asahi.toyama.jp
+fuchu.toyama.jp
+fukumitsu.toyama.jp
+funahashi.toyama.jp
+himi.toyama.jp
+imizu.toyama.jp
+inami.toyama.jp
+johana.toyama.jp
+kamiichi.toyama.jp
+kurobe.toyama.jp
+nakaniikawa.toyama.jp
+namerikawa.toyama.jp
+nanto.toyama.jp
+nyuzen.toyama.jp
+oyabe.toyama.jp
+taira.toyama.jp
+takaoka.toyama.jp
+tateyama.toyama.jp
+toga.toyama.jp
+tonami.toyama.jp
+toyama.toyama.jp
+unazuki.toyama.jp
+uozu.toyama.jp
+yamada.toyama.jp
+arida.wakayama.jp
+aridagawa.wakayama.jp
+gobo.wakayama.jp
+hashimoto.wakayama.jp
+hidaka.wakayama.jp
+hirogawa.wakayama.jp
+inami.wakayama.jp
+iwade.wakayama.jp
+kainan.wakayama.jp
+kamitonda.wakayama.jp
+katsuragi.wakayama.jp
+kimino.wakayama.jp
+kinokawa.wakayama.jp
+kitayama.wakayama.jp
+koya.wakayama.jp
+koza.wakayama.jp
+kozagawa.wakayama.jp
+kudoyama.wakayama.jp
+kushimoto.wakayama.jp
+mihama.wakayama.jp
+misato.wakayama.jp
+nachikatsuura.wakayama.jp
+shingu.wakayama.jp
+shirahama.wakayama.jp
+taiji.wakayama.jp
+tanabe.wakayama.jp
+wakayama.wakayama.jp
+yuasa.wakayama.jp
+yura.wakayama.jp
+asahi.yamagata.jp
+funagata.yamagata.jp
+higashine.yamagata.jp
+iide.yamagata.jp
+kahoku.yamagata.jp
+kaminoyama.yamagata.jp
+kaneyama.yamagata.jp
+kawanishi.yamagata.jp
+mamurogawa.yamagata.jp
+mikawa.yamagata.jp
+murayama.yamagata.jp
+nagai.yamagata.jp
+nakayama.yamagata.jp
+nanyo.yamagata.jp
+nishikawa.yamagata.jp
+obanazawa.yamagata.jp
+oe.yamagata.jp
+oguni.yamagata.jp
+ohkura.yamagata.jp
+oishida.yamagata.jp
+sagae.yamagata.jp
+sakata.yamagata.jp
+sakegawa.yamagata.jp
+shinjo.yamagata.jp
+shirataka.yamagata.jp
+shonai.yamagata.jp
+takahata.yamagata.jp
+tendo.yamagata.jp
+tozawa.yamagata.jp
+tsuruoka.yamagata.jp
+yamagata.yamagata.jp
+yamanobe.yamagata.jp
+yonezawa.yamagata.jp
+yuza.yamagata.jp
+abu.yamaguchi.jp
+hagi.yamaguchi.jp
+hikari.yamaguchi.jp
+hofu.yamaguchi.jp
+iwakuni.yamaguchi.jp
+kudamatsu.yamaguchi.jp
+mitou.yamaguchi.jp
+nagato.yamaguchi.jp
+oshima.yamaguchi.jp
+shimonoseki.yamaguchi.jp
+shunan.yamaguchi.jp
+tabuse.yamaguchi.jp
+tokuyama.yamaguchi.jp
+toyota.yamaguchi.jp
+ube.yamaguchi.jp
+yuu.yamaguchi.jp
+chuo.yamanashi.jp
+doshi.yamanashi.jp
+fuefuki.yamanashi.jp
+fujikawa.yamanashi.jp
+fujikawaguchiko.yamanashi.jp
+fujiyoshida.yamanashi.jp
+hayakawa.yamanashi.jp
+hokuto.yamanashi.jp
+ichikawamisato.yamanashi.jp
+kai.yamanashi.jp
+kofu.yamanashi.jp
+koshu.yamanashi.jp
+kosuge.yamanashi.jp
+minami-alps.yamanashi.jp
+minobu.yamanashi.jp
+nakamichi.yamanashi.jp
+nanbu.yamanashi.jp
+narusawa.yamanashi.jp
+nirasaki.yamanashi.jp
+nishikatsura.yamanashi.jp
+oshino.yamanashi.jp
+otsuki.yamanashi.jp
+showa.yamanashi.jp
+tabayama.yamanashi.jp
+tsuru.yamanashi.jp
+uenohara.yamanashi.jp
+yamanakako.yamanashi.jp
+yamanashi.yamanashi.jp
// ke : http://www.kenic.or.ke/index.php?option=com_content&task=view&id=117&Itemid=145
*.ke
@@ -1961,8 +3561,13 @@ gov.mr
// ms : http://en.wikipedia.org/wiki/.ms
ms
-// mt : https://www.nic.org.mt/dotmt/
-*.mt
+// mt : https://www.nic.org.mt/go/policy
+// Submitted by registry <help@nic.org.mt> 2013-11-19
+mt
+com.mt
+edu.mt
+net.mt
+org.mt
// mu : http://en.wikipedia.org/wiki/.mu
mu
@@ -2579,6 +4184,7 @@ name.my
// mz : http://www.gobin.info/domainname/mz-template.doc
*.mz
+!teledata.mz
// na : http://www.na-nic.com.na/
// http://www.info.na/domain/
@@ -2628,13 +4234,16 @@ other.nf
store.nf
// ng : http://psg.com/dns/ng/
-// Submitted by registry <randy@psg.com> 2008-06-17
-ac.ng
+ng
com.ng
edu.ng
-gov.ng
+name.ng
net.ng
org.ng
+sch.ng
+gov.ng
+mil.ng
+mobi.ng
// ni : http://www.nic.ni/dominios.htm
*.ni
@@ -2650,7 +4259,7 @@ bv.nl
// no : http://www.norid.no/regelverk/index.en.html
// The Norwegian registry has declined to notify us of updates. The web pages
// referenced below are the official source of the data. There is also an
-// announce mailing list:
+// announce mailing list:
// https://postlister.uninett.no/sympa/info/norid-diskusjon
no
// Norid generic domains : http://www.norid.no/regelverk/vedlegg-c.en.html
@@ -3435,17 +5044,16 @@ nu
*.nz
// om : http://en.wikipedia.org/wiki/.om
-*.om
-!mediaphone.om
-!nawrastelecom.om
-!nawras.om
-!omanmobile.om
-!omanpost.om
-!omantel.om
-!rakpetroleum.om
-!siemens.om
-!songfest.om
-!statecouncil.om
+om
+co.om
+com.om
+edu.om
+gov.om
+med.om
+museum.om
+net.om
+org.om
+pro.om
// org : http://en.wikipedia.org/wiki/.org
org
@@ -3714,6 +5322,9 @@ org.pn
edu.pn
net.pn
+// post : http://en.wikipedia.org/wiki/.post
+post
+
// pr : http://www.nic.pr/index.asp?f=1
pr
com.pr
@@ -3772,8 +5383,16 @@ ed.pw
go.pw
belau.pw
-// py : http://www.nic.py/faq_a.html#faq_b
-*.py
+// py : http://www.nic.py/pautas.html#seccion_9
+// Confirmed by registry 2012-10-03
+py
+com.py
+coop.py
+edu.py
+gov.py
+mil.py
+net.py
+org.py
// qa : http://domains.qa/en/
qa
@@ -4004,6 +5623,7 @@ net.sd
org.sd
edu.sd
med.sd
+tv.sd
gov.sd
info.sd
@@ -4051,7 +5671,7 @@ x.se
y.se
z.se
-// sg : http://www.nic.net.sg/sub_policies_agreement/2ld.html
+// sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines
sg
com.sg
net.sg
@@ -4060,9 +5680,13 @@ gov.sg
edu.sg
per.sg
-// sh : http://www.nic.sh/rules.html
-// list of 2nd level domains ?
+// sh : http://www.nic.sh/registrar.html
sh
+com.sh
+net.sh
+gov.sh
+org.sh
+mil.sh
// si : http://en.wikipedia.org/wiki/.si
si
@@ -4123,8 +5747,18 @@ store.st
// su : http://en.wikipedia.org/wiki/.su
su
-// sv : http://www.svnet.org.sv/svpolicy.html
-*.sv
+// sv : http://www.svnet.org.sv/niveldos.pdf
+sv
+com.sv
+edu.sv
+gob.sv
+org.sv
+red.sv
+
+// sx : http://en.wikipedia.org/wiki/.sx
+// Confirmed by registry <jcvignes@openregistry.com> 2012-05-31
+sx
+gov.sx
// sy : http://en.wikipedia.org/wiki/.sy
// see also: http://www.gobin.info/domainname/sy.doc
@@ -4157,8 +5791,7 @@ tel
tf
// tg : http://en.wikipedia.org/wiki/.tg
-// http://www.nic.tg/nictg/index.php implies no reserved 2nd-level domains,
-// although this contradicts wikipedia.
+// http://www.nic.tg/
tg
// th : http://en.wikipedia.org/wiki/.th
@@ -4172,7 +5805,7 @@ mi.th
net.th
or.th
-// tj : http://www.nic.tj/policy.htm
+// tj : http://www.nic.tj/policy.html
tj
ac.tj
biz.tj
@@ -4197,9 +5830,16 @@ tk
tl
gov.tl
-// tm : http://www.nic.tm/rules.html
-// list of 2nd level tlds ?
+// tm : http://www.nic.tm/local.html
tm
+com.tm
+co.tm
+org.tm
+net.tm
+nom.tm
+gov.tm
+mil.tm
+edu.tm
// tn : http://en.wikipedia.org/wiki/.tn
// http://whois.ati.tn/
@@ -4286,101 +5926,133 @@ club.tw
組織.tw
商業.tw
-// tz : http://en.wikipedia.org/wiki/.tz
-// Submitted by registry <randy@psg.com> 2008-06-17
-// Updated from http://www.tznic.or.tz/index.php/domains.html 2010-10-25
+// tz : http://www.tznic.or.tz/index.php/domains
+// Confirmed by registry <manager@tznic.or.tz> 2013-01-22
ac.tz
co.tz
go.tz
+hotel.tz
+info.tz
+me.tz
mil.tz
+mobi.tz
ne.tz
or.tz
sc.tz
+tv.tz
-// ua : http://www.nic.net.ua/
+// ua : https://hostmaster.ua/policy/?ua
+// Submitted by registry <dk@cctld.ua> 2012-04-27
ua
+// ua 2LD
com.ua
edu.ua
gov.ua
in.ua
net.ua
org.ua
-// ua geo-names
+// ua geographic names
+// https://hostmaster.ua/2ld/
cherkassy.ua
+cherkasy.ua
chernigov.ua
+chernihiv.ua
+chernivtsi.ua
chernovtsy.ua
ck.ua
cn.ua
+cr.ua
crimea.ua
cv.ua
dn.ua
dnepropetrovsk.ua
+dnipropetrovsk.ua
+dominic.ua
donetsk.ua
dp.ua
if.ua
ivano-frankivsk.ua
kh.ua
+kharkiv.ua
kharkov.ua
kherson.ua
khmelnitskiy.ua
+khmelnytskyi.ua
kiev.ua
kirovograd.ua
km.ua
kr.ua
+krym.ua
ks.ua
kv.ua
+kyiv.ua
lg.ua
+lt.ua
lugansk.ua
lutsk.ua
+lv.ua
lviv.ua
mk.ua
+mykolaiv.ua
nikolaev.ua
od.ua
+odesa.ua
odessa.ua
pl.ua
poltava.ua
+rivne.ua
rovno.ua
rv.ua
+sb.ua
sebastopol.ua
+sevastopol.ua
+sm.ua
sumy.ua
te.ua
ternopil.ua
+uz.ua
uzhgorod.ua
vinnica.ua
+vinnytsia.ua
vn.ua
+volyn.ua
+yalta.ua
zaporizhzhe.ua
-zp.ua
+zaporizhzhia.ua
zhitomir.ua
+zhytomyr.ua
+zp.ua
zt.ua
// Private registries in .ua
co.ua
pp.ua
-// ug : http://www.registry.co.ug/
+// ug : https://www.registry.co.ug/
ug
co.ug
+or.ug
ac.ug
sc.ug
go.ug
ne.ug
-or.ug
+com.ug
+org.ug
// uk : http://en.wikipedia.org/wiki/.uk
+// Submitted by registry <noc@nominet.org.uk> 2012-10-02
+// and tweaked by us pending further consultation.
*.uk
*.sch.uk
!bl.uk
!british-library.uk
-!icnet.uk
!jet.uk
!mod.uk
+!national-library-scotland.uk
!nel.uk
-!nhs.uk
!nic.uk
!nls.uk
-!national-library-scotland.uk
!parliament.uk
-!police.uk
// us : http://en.wikipedia.org/wiki/.us
us
@@ -4464,7 +6136,7 @@ k12.de.us
k12.fl.us
k12.ga.us
k12.gu.us
-// k12.hi.us Hawaii has a state-wide DOE login: bug 614565
+// k12.hi.us Bug 614565 - Hawaii has a state-wide DOE login
k12.ia.us
k12.id.us
k12.il.us
@@ -4495,7 +6167,7 @@ k12.pa.us
k12.pr.us
k12.ri.us
k12.sc.us
-k12.sd.us
+// k12.sd.us Bug 934131 - Removed at request of James Booze <James.Booze@k12.sd.us>
k12.tn.us
k12.tx.us
k12.ut.us
@@ -4616,25 +6288,32 @@ lib.vt.us
lib.va.us
lib.wa.us
lib.wi.us
-lib.wv.us
+// lib.wv.us Bug 941670 - Removed at request of Larry W Arnold <arnold@wvlc.lib.wv.us>
lib.wy.us
-// k12.ma.us contains school districts in Massachusetts. The 4LDs are
+// k12.ma.us contains school districts in Massachusetts. The 4LDs are
// managed indepedently except for private (PVT), charter (CHTR) and
-// parochial (PAROCH) schools. Those are delegated dorectly to the
+// parochial (PAROCH) schools. Those are delegated dorectly to the
// 5LD operators. <k12-ma-hostmaster _ at _ rsuc.gweep.net>
pvt.k12.ma.us
chtr.k12.ma.us
paroch.k12.ma.us
-// uy : http://www.antel.com.uy/
-*.uy
+// uy : http://www.nic.org.uy/
+uy
+com.uy
+edu.uy
+gub.uy
+mil.uy
+net.uy
+org.uy
-// uz : http://www.reg.uz/registerr.html
-// are there other 2nd level tlds ?
+// uz : http://www.reg.uz/
uz
-com.uz
co.uz
+com.uz
+net.uz
+org.uz
// va : http://en.wikipedia.org/wiki/.va
va
@@ -4649,8 +6328,19 @@ gov.vc
mil.vc
edu.vc
-// ve : http://registro.nic.ve/nicve/registro/index.html
-*.ve
+// ve : https://registro.nic.ve/
+// Confirmed by registry 2012-10-04
+ve
+co.ve
+com.ve
+e12.ve
+edu.ve
+gov.ve
+info.ve
+mil.ve
+net.ve
+org.ve
+web.ve
// vg : http://en.wikipedia.org/wiki/.vg
vg
@@ -4708,13 +6398,13 @@ yt
// <URL>
// xn--mgbaam7a8h ("Emerat" Arabic) : AE
-//http://nic.ae/english/arabicdomain/rules.jsp
+// http://nic.ae/english/arabicdomain/rules.jsp
امارات
-// xn--54b7fta0cc ("Bangla" Bangla) : BD
+// xn--54b7fta0cc ("Bangla" Bangla) : BD
বাংলা
-// xn--fiqs8s ("China" Chinese-Han-Simplified <.Zhonggou>) : CN
+// xn--fiqs8s ("China" Chinese-Han-Simplified <.Zhonggou>) : CN
// CNNIC
// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
中国
@@ -4724,60 +6414,60 @@ yt
// http://cnnic.cn/html/Dir/2005/10/11/3218.htm
中國
-// xn--lgbbat1ad8j ("Algeria / Al Jazair" Arabic) : DZ
+// xn--lgbbat1ad8j ("Algeria / Al Jazair" Arabic) : DZ
الجزائر
// xn--wgbh1c ("Egypt" Arabic .masr) : EG
// http://www.dotmasr.eg/
مصر
-// xn--node ("ge" Georgian (Mkhedruli)) : GE
+// xn--node ("ge" Georgian (Mkhedruli)) : GE
გე
// xn--j6w193g ("Hong Kong" Chinese-Han) : HK
// https://www2.hkirc.hk/register/rules.jsp
香港
-// xn--h2brj9c ("Bharat" Devanagari) : IN
+// xn--h2brj9c ("Bharat" Devanagari) : IN
// India
भारत
-// xn--mgbbh1a71e ("Bharat" Arabic) : IN
+// xn--mgbbh1a71e ("Bharat" Arabic) : IN
// India
بھارت
-// xn--fpcrj9c3d ("Bharat" Telugu) : IN
+// xn--fpcrj9c3d ("Bharat" Telugu) : IN
// India
భారత్
-// xn--gecrj9c ("Bharat" Gujarati) : IN
+// xn--gecrj9c ("Bharat" Gujarati) : IN
// India
ભારત
-// xn--s9brj9c ("Bharat" Gurmukhi) : IN
+// xn--s9brj9c ("Bharat" Gurmukhi) : IN
// India
ਭਾਰਤ
-// xn--45brj9c ("Bharat" Bengali) : IN
+// xn--45brj9c ("Bharat" Bengali) : IN
// India
ভারত
-// xn--xkc2dl3a5ee0h ("India" Tamil) : IN
+// xn--xkc2dl3a5ee0h ("India" Tamil) : IN
// India
இந்தியா
-// xn--mgba3a4f16a ("Iran" Persian) : IR
+// xn--mgba3a4f16a ("Iran" Persian) : IR
ایران
-// xn--mgba3a4fra ("Iran" Arabic) : IR
+// xn--mgba3a4fra ("Iran" Arabic) : IR
ايران
-//xn--mgbayh7gpa ("al-Ordon" Arabic) JO
-//National Information Technology Center (NITC)
-//Royal Scientific Society, Al-Jubeiha
+// xn--mgbayh7gpa ("al-Ordon" Arabic) : JO
+// National Information Technology Center (NITC)
+// Royal Scientific Society, Al-Jubeiha
الاردن
-// xn--3e0b707e ("Republic of Korea" Hangul) : KR
+// xn--3e0b707e ("Republic of Korea" Hangul) : KR
한국
// xn--fzc2c9e2c ("Lanka" Sinhalese-Sinhala) : LK
@@ -4788,10 +6478,10 @@ yt
// http://nic.lk
இலங்கை
-// xn--mgbc0a9azcg ("Morocco / al-Maghrib" Arabic) : MA
+// xn--mgbc0a9azcg ("Morocco / al-Maghrib" Arabic) : MA
المغرب
-// xn--mgb9awbf ("Oman" Arabic) : OM
+// xn--mgb9awbf ("Oman" Arabic) : OM
عمان
// xn--ygbi2ammx ("Falasteen" Arabic) : PS
@@ -4799,7 +6489,7 @@ yt
// http://www.pnina.ps
فلسطين
-// xn--90a3ac ("srb" Cyrillic) : RS
+// xn--90a3ac ("srb" Cyrillic) : RS
срб
// xn--p1ai ("rf" Russian-Cyrillic) : RU
@@ -4814,19 +6504,19 @@ yt
// http://www.nic.net.sa/
السعودية
-// xn--mgberp4a5d4a87g ("AlSaudiah" Arabic) variant : SA
+// xn--mgberp4a5d4a87g ("AlSaudiah" Arabic) variant : SA
السعودیة
-// xn--mgbqly7c0a67fbc ("AlSaudiah" Arabic) variant : SA
+// xn--mgbqly7c0a67fbc ("AlSaudiah" Arabic) variant : SA
السعودیۃ
-// xn--mgbqly7cvafr ("AlSaudiah" Arabic) variant : SA
+// xn--mgbqly7cvafr ("AlSaudiah" Arabic) variant : SA
السعوديه
-// xn--ogbpf8fl ("Syria" Arabic) : SY
+// xn--ogbpf8fl ("Syria" Arabic) : SY
سورية
-// xn--mgbtf8fl ("Syria" Arabic) variant : SY
+// xn--mgbtf8fl ("Syria" Arabic) variant : SY
سوريا
// xn--yfro4i67o Singapore ("Singapore" Chinese-Han) : SG
@@ -4851,13 +6541,13 @@ yt
// http://www.twnic.net/english/dn/dn_07a.htm
台湾
-// xn--nnx388a ("Taiwan") variant : TW
+// xn--nnx388a ("Taiwan") variant : TW
臺灣
-// xn--j1amh ("ukr" Cyrillic) : UA
+// xn--j1amh ("ukr" Cyrillic) : UA
укр
-// xn--mgb2ddes ("AlYemen" Arabic) : YE
+// xn--mgb2ddes ("AlYemen" Arabic) : YE
اليمن
// xxx : http://icmregistry.com
@@ -4875,30 +6565,464 @@ xxx
// zw : http://en.wikipedia.org/wiki/.zw
*.zw
-// ===END ICANN DOMAINS===
-// ===BEGIN PRIVATE DOMAINS===
-// info.at : http://www.info.at/
-biz.at
-info.at
+// xn--80asehdb : 2013-07-14 CORE Association
+онлайн
-// priv.at : http://www.nic.priv.at/
-// Submitted by registry <lendl@nic.at> 2008-06-09
-priv.at
+// xn--80aswg : 2013-07-14 CORE Association
+сайт
-// co.ca : http://registry.co.ca
-co.ca
+// xn--ngbc5azd : 2013-07-14 International Domain Registry Pty. Ltd.
+شبكة
+
+// xn--unup4y : 2013-07-14 Spring Fields, LLC
+游戏
+
+// xn--vhquv : 2013-08-28 Dash McCook, LLC
+企业
+
+// camera : 2013-08-28 Atomic Maple, LLC
+camera
+
+// clothing : 2013-08-28 Steel Lake, LLC
+clothing
+
+// lighting : 2013-08-28 John McCook, LLC
+lighting
+
+// singles : 2013-08-28 Fern Madison, LLC
+singles
+
+// ventures : 2013-08-28 Binky Lake, LLC
+ventures
+
+// voyage : 2013-08-28 Ruby House, LLC
+voyage
+
+// guru : 2013-08-28 Pioneer Cypress, LLC
+guru
+
+// holdings : 2013-08-28 John Madison, LLC
+holdings
+
+// equipment : 2013-08-28 Corn Station, LLC
+equipment
+
+// bike : 2013-08-28 Grand Hollow, LLC
+bike
+
+// estate : 2013-08-28 Trixy Park, LLC
+estate
+
+// tattoo : 2013-08-30 Uniregistry,Corp.
+tattoo
+
+// xn--3ds443g : 2013-09-09 TLD Registry Limited
+在线
+
+// xn--fiq228c5hs : 2013-09-09 TLD Registry Limited
+中文网
+
+// land : 2013-09-10 Pine Moon, LLC
+land
+
+// plumbing : 2013-09-10 Spring Tigers, LLC
+plumbing
+
+// contractors : 2013-09-10 Magic Woods, LLC
+contractors
+
+// sexy : 2013-09-11 Uniregistry,Corp.
+sexy
+
+// menu : 2013-09-11 Wedding TLD2, LLC
+menu
+
+// xn--rhqv96g : 2013-09-11 Stable Tone Limited
+世界
+
+// uno : 2013-09-11 Dot Latin, LLC
+uno
+
+// gallery : 2013-09-13 Sugar House, LLC
+gallery
+
+// technology : 2013-09-13 Auburn Falls
+technology
+
+// xn--3bst00m : 2013-09-13 Eagle Horizon Limited
+集团
+
+// reviews : 2013-09-13 Extra Cover, LLC
+reviews
+
+// guide : 2013-09-13 Snow Moon, LLC
+guide
+
+// xn--6qq986b3x1 : 2013-09-13 Tycoon Treasure Limited
+我爱你
+
+// graphics : 2013-09-13 Over Madison, LLC
+graphics
+
+// construction : 2013-09-13 Fox Dynamite, LLC
+construction
+
+// onl : 2013-09-16 I-Registry Ltd.
+onl
+
+// xn--q9jyb4c : 2013-09-17 Charleston Road Registry
+みんな
+
+// diamonds : 2013-09-23 John Edge, LLC
+diamonds
+
+// kiwi : 2013-09-23 Dot Kiwi Limited
+kiwi
+
+// enterprises : 2013-09-23 Snow Oaks LLC
+enterprises
+
+// today : 2013-09-23 Pearl Woods, LLC
+today
+
+// futbol : 2013-09-23 Atomic Falls, LLC
+futbol
+
+// photography : 2013-09-23 Sugar Glen, LLC
+photography
+
+// tips : 2013-09-23 Corn Willow, LLC
+tips
+
+// directory : 2013-09-23 Extra Madison, LLC
+directory
+
+// kitchen : 2013-09-23 Just Goodbye, LLC
+kitchen
+
+// xn--6frz82g : 2013-09-24 Afilias Limited
+移动
+
+// kim : 2013-09-24 Afilias Limited
+kim
+
+// xn--cg4bki : 2013-09-27 Samsung SDS Co., LTD
+삼성
+
+// monash : 2013-10-01 Monash University
+monash
+
+// wed : 2013-10-02 Atgron, Inc.
+wed
+
+// pink : 2013-10-02 Afilias Limited
+pink
+
+// ruhr : 2013-10-02 regiodot GmbH & Co. KG
+ruhr
+
+// buzz : 2013-10-03 DOTSTRATEGY CO.
+buzz
+
+// careers : 2013-10-03 Wild Corner, LLC
+careers
+
+// shoes : 2013-10-03 Binky Galley, LLC
+shoes
+
+// xn--4gbrim : 2013-10-07 Suhub Electronic Establishment
+موقع
+
+// career : 2013-10-09 dotCareer, LLC
+career
+
+// otsuka : 2013-10-11 Otsuka Holdings Co. Ltd.
+otsuka
+
+// xn--fiQ64b : 2013-10-14 CITIC Group Corporation
+中信
+
+// wed : 2013-10-02 Atgron, Inc.
+wed
+
+// pink : 2013-10-02 Afilias Limited
+pink
+
+// ruhr : 2013-10-02 regiodot GmbH & Co. KG
+ruhr
+
+// buzz : 2013-10-03 DOTSTRATEGY CO.
+buzz
+
+// careers : 2013-10-03 Wild Corner, LLC
+careers
+
+// shoes : 2013-10-03 Binky Galley, LLC
+shoes
+
+// xn--4gbrim : 2013-10-07 Suhub Electronic Establishment
+موقع
+
+// career : 2013-10-09 dotCareer, LLC
+career
+
+// otsuka : 2013-10-11 Otsuka Holdings Co. Ltd.
+otsuka
+
+// xn--fiQ64b : 2013-10-14 CITIC Group Corporation
+中信
+
+// gift : 2013-10-18 Uniregistry Corp.
+gift
+
+// recipes : 2013-10-18 Grand Island, LLC
+recipes
+
+// coffee : 2013-10-18 Trixy Cover, LLC
+coffee
+
+// luxury : 2013-10-18 Luxury Partners, LLC
+luxury
+
+// domains : 2013-10-18 Sugar Cross, LLC
+domains
+
+// photos : 2013-10-18 Sea Corner, LLC
+photos
+
+// limo : 2013-10-18 Hidden Frostbite, LLC
+limo
+
+// viajes : 2013-10-18 Black Madison, LLC
+viajes
+
+// wang : 2013-10-24 Zodiac Leo Limited
+wang
+
+// democrat : 2013-10-24 United TLD Holdco Ltd.
+democrat
+
+// mango : 2013-10-25 PUNTO FA S.L.
+mango
+
+// cab : 2013-10-25 Half Sunset, LLC
+cab
+
+// support : 2013-10-25 Grand Orchard, LLC
+support
+
+// dance : 2013-10-25 United TLD Holdco Ltd.
+dance
+
+// nagoya : 2013-10-25 GMO Registry, Inc.
+nagoya
+
+// computer : 2013-10-25 Pine Mill, LLC
+computer
+
+// wien : 2013-10-28 punkt.wien GmbH
+wien
+
+// berlin : 2013-10-31 dotBERLIN GmbH & Co. KG
+berlin
+
+// codes : 2013-10-31 Puff Willow, LLC
+codes
+
+// email : 2013-10-31 Spring Madison, LLC
+email
+
+// xn--mgbab2bd : 2013-10-31 CORE Association
+بازار
+
+// repair : 2013-11-07 Lone Sunset, LLC
+repair
+
+// holiday : 2013-11-07 Goose Woods, LLC
+holiday
+
+// center : 2013-11-07 Tin Mill, LLC
+center
+
+// systems : 2013-11-07 Dash Cypress, LLC
+systems
+
+// wiki : 2013-11-07 Top Level Design, LLC
+wiki
+
+// ceo : 2013-11-07 CEOTLD Pty Ltd
+ceo
+
+// international : 2013-11-07 Wild Way, LLC
+international
+
+// solar : 2013-11-07 Ruby Town, LLC
+solar
+
+// company : 2013-11-07 Silver Avenue, LLC
+company
+
+// education : 2013-11-07 Brice Way, LLC
+education
+
+// training : 2013-11-07 Wild Willow, LLC
+training
+
+// academy : 2013-11-07 Half Oaks, LLC
+academy
+
+// marketing : 2013-11-07 Fern Pass, LLC
+marketing
+
+// florist : 2013-11-08 Half Cypress, LLC
+florist
+
+// solutions : 2013-11-07 Silver Cover, LLC
+solutions
+
+// build : 2013-11-07 Plan Bee LLC
+build
+
+// institute : 2013-11-07 Outer Maple, LLC
+institute
+
+// builders : 2013-11-07 Atomic Madison, LLC
+builders
+
+// red : 2013-11-07 Afilias Limited
+red
+
+// blue : 2013-11-07 Afilias Limited
+blue
+
+// ninja : 2013-11-07 United TLD Holdco Ltd.
+ninja
+
+// business : 2013-11-07 Spring Cross, LLC
+business
+
+// gal : 2013-11-07 Asociación puntoGAL
+gal
+
+// social : 2013-11-07 United TLD Holdco Ltd.
+social
+
+// house : 2013-11-07 Sugar Park, LLC
+house
+
+// camp : 2013-11-07 Delta Dynamite, LLC
+camp
+
+// immobilien : 2013-11-07 United TLD Holdco Ltd.
+immobilien
+
+// moda : 2013-11-07 United TLD Holdco Ltd.
+moda
+
+// glass : 2013-11-07 Black Cover, LLC
+glass
+
+// management : 2013-11-07 John Goodbye, LLC
+management
+
+// kaufen : 2013-11-07 United TLD Holdco Ltd.
+kaufen
+
+// farm : 2013-11-07 Just Maple, LLC
+farm
+
+// xn--55qw42g : 2013-11-08 China Organizational Name Administration Center
+公益
+
+// xn--zfr164b : 2013-11-08 China Organizational Name Administration Center
+政务
+
+// club : 2013-11-08 .CLUB DOMAINS, LLC
+club
+
+// voting : 2013-11-13 Valuetainment Corp.
+voting
+
+// TOKYO : 2013-11-13 GMO Registry, Inc.
+TOKYO
+
+// moe : 2013-11-13 Interlink Co., Ltd.
+moe
+
+
+// ===END ICANN DOMAINS===
+// ===BEGIN PRIVATE DOMAINS===
+
+// Amazon CloudFront : https://aws.amazon.com/cloudfront/
+// Submitted by Donavan Miller <donavanm@amazon.com> 2013-03-22
+cloudfront.net
+
+// Amazon Elastic Compute Cloud: https://aws.amazon.com/ec2/
+// Submitted by Osman Surkatty <osmans@amazon.com> 2013-04-02
+compute.amazonaws.com
+us-east-1.amazonaws.com
+compute-1.amazonaws.com
+z-1.compute-1.amazonaws.com
+z-2.compute-1.amazonaws.com
+ap-northeast-1.compute.amazonaws.com
+ap-southeast-1.compute.amazonaws.com
+ap-southeast-2.compute.amazonaws.com
+eu-west-1.compute.amazonaws.com
+sa-east-1.compute.amazonaws.com
+us-gov-west-1.compute.amazonaws.com
+us-west-1.compute.amazonaws.com
+us-west-2.compute.amazonaws.com
+
+// Amazon Elastic Beanstalk : https://aws.amazon.com/elasticbeanstalk/
+// Submitted by Adam Stein <astein@amazon.com> 2013-04-02
+elasticbeanstalk.com
+
+// Amazon Elastic Load Balancing : https://aws.amazon.com/elasticloadbalancing/
+// Submitted by Scott Vidmar <svidmar@amazon.com> 2013-03-27
+elb.amazonaws.com
+
+// Amazon S3 : https://aws.amazon.com/s3/
+// Submitted by Courtney Eckhardt <coec@amazon.com> 2013-03-22
+s3.amazonaws.com
+s3-us-west-2.amazonaws.com
+s3-us-west-1.amazonaws.com
+s3-eu-west-1.amazonaws.com
+s3-ap-southeast-1.amazonaws.com
+s3-ap-southeast-2.amazonaws.com
+s3-ap-northeast-1.amazonaws.com
+s3-sa-east-1.amazonaws.com
+s3-us-gov-west-1.amazonaws.com
+s3-fips-us-gov-west-1.amazonaws.com
+s3-website-us-east-1.amazonaws.com
+s3-website-us-west-2.amazonaws.com
+s3-website-us-west-1.amazonaws.com
+s3-website-eu-west-1.amazonaws.com
+s3-website-ap-southeast-1.amazonaws.com
+s3-website-ap-southeast-2.amazonaws.com
+s3-website-ap-northeast-1.amazonaws.com
+s3-website-sa-east-1.amazonaws.com
+s3-website-us-gov-west-1.amazonaws.com
+
+// BetaInABox
+// Submitted by adrian@betainabox.com 2012-09-13
+betainabox.com
// CentralNic : http://www.centralnic.com/names/domains
-// Confirmed by registry <gavin.brown@centralnic.com> 2008-06-09
+// Submitted by registry <gavin.brown@centralnic.com> 2012-09-27
+ae.org
ar.com
br.com
cn.com
+com.de
de.com
eu.com
gb.com
+gb.net
gr.com
hu.com
+hu.net
+jp.net
jpn.com
kr.com
no.com
@@ -4906,44 +7030,36 @@ qc.com
ru.com
sa.com
se.com
+se.net
uk.com
+uk.net
us.com
+us.org
uy.com
za.com
-gb.net
-jp.net
-se.net
-uk.net
-ae.org
-us.org
-com.de
-
-// Opera Software, A.S.A.
-// Requested by Yngve Pettersen <yngve@opera.com> 2009-11-26
-operaunite.com
-
-// Google, Inc.
-// Requested by Eduardo Vela <evn@google.com> 2010-09-06
-appspot.com
-
-// iki.fi : Submitted by Hannu Aronsson <haa@iki.fi> 2009-11-05
-iki.fi
// c.la : http://www.c.la/
c.la
-// ZaNiC : http://www.za.net/
-// Confirmed by registry <hostmaster@nic.za.net> 2009-10-03
-za.net
-za.org
+// cloudControl : https://www.cloudcontrol.com/
+// Submitted by Tobias Wilken <tw@cloudcontrol.com> 2013-07-23
+cloudcontrolled.com
+cloudcontrolapp.com
+
+// co.ca : http://registry.co.ca/
+co.ca
// CoDNS B.V.
-// Added 2010-05-23.
co.nl
co.no
-// Mainseek Sp. z o.o. : http://www.co.pl/
-co.pl
+// Cupcake : https://cupcake.io/
+// Submitted by Jonathan Rudenberg <jonathan@cupcake.io> 2013-10-08
+cupcake.is
+
+// DreamHost : http://www.dreamhost.com/
+// Submitted by Andrew Farmer <andrew.farmer@dreamhost.com> 2012-10-02
+dreamhosters.com
// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/
dyndns-at-home.com
@@ -5226,4 +7342,104 @@ webhop.org
worse-than.tv
writesthisblog.com
+// Fastly Inc. http://www.fastly.com/
+// Submitted by Vladimir Vuksan <vladimir@fastly.com> 2013-05-31
+a.ssl.fastly.net
+b.ssl.fastly.net
+global.ssl.fastly.net
+a.prod.fastly.net
+global.prod.fastly.net
+
+// GitHub, Inc.
+// Submitted by Ben Toews <btoews@github.com> 2013-04-18
+github.io
+
+// GlobeHosting, Inc.
+// Submitted by Zoltan Egresi <egresi@globehosting.com> 2013-07-12
+ro.com
+
+// Google, Inc.
+// Submitted by Eduardo Vela <evn@google.com> 2012-10-24
+appspot.com
+blogspot.be
+blogspot.bj
+blogspot.ca
+blogspot.cf
+blogspot.ch
+blogspot.co.at
+blogspot.co.il
+blogspot.co.nz
+blogspot.co.uk
+blogspot.com
+blogspot.com.ar
+blogspot.com.au
+blogspot.com.br
+blogspot.com.es
+blogspot.cv
+blogspot.cz
+blogspot.de
+blogspot.dk
+blogspot.fi
+blogspot.fr
+blogspot.gr
+blogspot.hk
+blogspot.hu
+blogspot.ie
+blogspot.in
+blogspot.it
+blogspot.jp
+blogspot.kr
+blogspot.mr
+blogspot.mx
+blogspot.nl
+blogspot.no
+blogspot.pt
+blogspot.re
+blogspot.ro
+blogspot.se
+blogspot.sg
+blogspot.sk
+blogspot.td
+blogspot.tw
+codespot.com
+googleapis.com
+googlecode.com
+
+// Heroku : https://www.heroku.com/
+// Submitted by Tom Maher <tmaher@heroku.com> 2013-05-02
+herokuapp.com
+herokussl.com
+
+// iki.fi
+// Submitted by Hannu Aronsson <haa@iki.fi> 2009-11-05
+iki.fi
+
+// info.at : http://www.info.at/
+biz.at
+info.at
+
+// Michau Enterprises Limited : http://www.co.pl/
+co.pl
+
+// NYC.mn : http://www.information.nyc.mn
+// Submitted by Matthew Brown <mattbrown@nyc.mn> 2013-03-11
+nyc.mn
+
+// Opera Software, A.S.A.
+// Submitted by Yngve Pettersen <yngve@opera.com> 2009-11-26
+operaunite.com
+
+// Red Hat, Inc. OpenShift : https://openshift.redhat.com/
+// Submitted by Tim Kramer <tkramer@rhcloud.com> 2012-10-24
+rhcloud.com
+
+// priv.at : http://www.nic.priv.at/
+// Submitted by registry <lendl@nic.at> 2008-06-09
+priv.at
+
+// ZaNiC : http://www.za.net/
+// Submitted by registry <hostmaster@nic.za.net> 2009-10-03
+za.net
+za.org
+
// ===END PRIVATE DOMAINS===
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index f21e710e..d47693a1 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -13,7 +13,7 @@ DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
DOC_SOURCE_DIR=../../libsoup
# Extra options to supply to gtkdoc-scan.
-SCAN_OPTIONS=--deprecated-guards=LIBSOUP_DISABLE_DEPRECATED --rebuild-types
+SCAN_OPTIONS=--deprecated-guards=SOUP_DISABLE_DEPRECATED --rebuild-types --ignore-decorators='SOUP_DEPRECATED\w*\s*\([^)]*\)|SOUP_DEPRECATED\w*|SOUP_AVAILABLE\w*'
# Extra options to supply to gtkdoc-scangobj.
SCANGOBJ_OPTIONS=
@@ -29,16 +29,23 @@ HFILE_GLOB=
CFILE_GLOB=
# Header files to ignore when scanning.
-IGNORE_HFILES= soup.h soup-marshal.h soup-enum-types.h \
+IGNORE_HFILES= soup.h soup-enum-types.h \
soup-message-private.h soup-session-private.h \
soup-auth-basic.h soup-auth-digest.h soup-auth-ntlm.h \
- soup-connection.h soup-connection-ntlm.h \
- soup-dns.h soup-auth-manager.h soup-auth-manager-ntlm.h \
- soup-message-queue.h soup-path-map.h soup-ssl.h \
+ soup-connection.h soup-connection-auth.h \
+ soup-message-queue.h soup-path-map.h soup-gnome-features.h \
soup-proxy-resolver.h soup-proxy-resolver-gnome.h \
soup-proxy-resolver-static.h soup-directory-input-stream.h \
soup-http-input-stream.h soup-password-manager.h \
- soup-password-manager-gnome.h
+ soup-password-manager-gnome.h soup-converter-wrapper.h \
+ soup-body-input-stream.h soup-body-output-stream.h \
+ soup-client-input-stream.h soup-content-processor.h \
+ soup-content-sniffer-stream.h soup-io-stream.h \
+ soup-cache-input-stream.h soup-filter-input-stream.h \
+ soup-cookie-jar-sqlite.h soup-requester.h soup-tld-private.h \
+ soup-misc-private.h soup-proxy-uri-resolver.h \
+ soup-proxy-resolver-wrapper.h soup-proxy-uri-resolver.h \
+ soup-cache-private.h
# Images to copy into HTML directory.
HTML_IMAGES =
@@ -47,7 +54,9 @@ HTML_IMAGES =
content_files = \
build-howto.xml \
client-howto.xml \
- server-howto.xml
+ request-howto.xml \
+ server-howto.xml \
+ session-porting.xml
# Other files to distribute.
extra_files =
@@ -64,7 +73,6 @@ GTKDOC_CFLAGS = \
GTKDOC_LIBS = \
$(top_builddir)/libsoup/libsoup-2.4.la \
- $(top_builddir)/libsoup/libsoup-gnome-2.4.la \
$(GLIB_LIBS)
# include common portion ...
diff --git a/docs/reference/build-howto.xml b/docs/reference/build-howto.xml
index 975dfd55..bec9a454 100644
--- a/docs/reference/build-howto.xml
+++ b/docs/reference/build-howto.xml
@@ -35,41 +35,52 @@ that first appeared in version 2.4") and is essentially just part of
the package name.
</para>
+</refsect2>
+
+<refsect2>
+<title>API Availability and Deprecation Warnings</title>
+
<para>
-If you are using any of the GNOME-specific features of
-<application>libsoup</application> (such as automatic proxy
-configuration), you must require
-"<literal>libsoup-gnome-2.4</literal>" instead:
+If you want to restrict your program to a particular
+<application>libsoup</application> version or range of versions, you
+can define <link
+linkend="SOUP-VERSION-MIN-REQUIRED:CAPS"><literal>SOUP_VERSION_MIN_REQUIRED</literal></link>
+and/or <link
+linkend="SOUP-VERSION-MAX-ALLOWED:CAPS"><literal>SOUP_VERSION_MAX_ALLOWED</literal></link>.
+Eg:
</para>
<informalexample><programlisting>
-PKG_CHECK_MODULES(LIBSOUP, [libsoup-gnome-2.4 >= 2.26])
-AC_SUBST(LIBSOUP_CFLAGS)
-AC_SUBST(LIBSOUP_LIBS)
+LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_36"
+LIBSOUP_CFLAGS="$LIBSOUP_CFLAGS -DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_2_40"
</programlisting></informalexample>
<para>
-You can also make <application>libsoup-gnome</application> an optional
-dependency:
+The <literal>SOUP_VERSION_MIN_REQUIRED</literal> declaration states
+that the code is not expected to compile on versions of
+<application>libsoup</application> older than the indicated version
+(here, 2.36), and so the compiler should print warnings if the code
+uses functions that were deprecated as of that release.
</para>
-<informalexample><programlisting>
-PKG_CHECK_MODULES(LIBSOUP_GNOME,
- [libsoup-gnome-2.4 >= 2.26],
- [LIBSOUP_CFLAGS="$LIBSOUP_GNOME_CFLAGS"
- LIBSOUP_LIBS="$LIBSOUP_GNOME_LIBS"
- AC_DEFINE(HAVE_LIBSOUP_GNOME, 1, [Have libsoup-gnome])],
- [PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26])])
-AC_SUBST(LIBSOUP_CFLAGS)
-AC_SUBST(LIBSOUP_LIBS)
-</programlisting></informalexample>
+<para>
+The <literal>SOUP_VERSION_MAX_ALLOWED</literal> declaration states
+that the code <emphasis>is</emphasis> expected to compile on versions
+of <application>libsoup</application> up to the indicated version
+(here, 2.40), and so, when compiling the program against a newer
+version than that, the compiler should print warnings if the code uses
+functions that did not yet exist in the max-allowed release.
+</para>
<para>
-This will allow the application to be built with either plain
-<application>libsoup</application> or with
-<application>libsoup-gnome</application>, and it will define the C
-preprocessor symbol <literal>HAVE_LIBSOUP_GNOME</literal> if
-<application>libsoup-gnome</application> features are available.
+You can use <link
+linkend="SOUP-CHECK-VERSION:CAPS"><literal>SOUP_CHECK_VERSION</literal></link>
+to check the version of libsoup at compile time, to compile different
+code for different <application>libsoup</application> versions. (If
+you are setting <literal>SOUP_VERSION_MIN_REQUIRED</literal> and
+<literal>SOUP_VERSION_MAX_ALLOWED</literal> to different versions, as
+in the example above, then you almost certainly need to be doing
+this.)
</para>
</refsect2>
@@ -86,18 +97,8 @@ Code using <application>libsoup</application> should do:
</programlisting></informalexample>
<para>
-or, for <application>libsoup-gnome</application>:
-</para>
-
-<informalexample><programlisting>
-#include &lt;libsoup/soup-gnome.h&gt;
-</programlisting></informalexample>
-
-<para>
-Including individual headers besides the two main header files is not
-recommended. You may include both <literal>soup.h</literal> and
-<literal>soup-gnome.h</literal> (though this is not required; the
-latter automatically includes the former).
+Including individual headers rather than <literal>soup.h</literal> is not
+recommended.
</para>
</refsect2>
diff --git a/docs/reference/client-howto.xml b/docs/reference/client-howto.xml
index a53f2ac3..e694cb72 100644
--- a/docs/reference/client-howto.xml
+++ b/docs/reference/client-howto.xml
@@ -3,16 +3,26 @@
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
<refentry id="libsoup-client-howto">
<refmeta>
-<refentrytitle>Soup Client Basics</refentrytitle>
+<refentrytitle>libsoup Client Basics</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>LIBSOUP Library</refmiscinfo>
</refmeta>
<refnamediv>
-<refname>Soup Client Basics</refname><refpurpose>Client-side tutorial</refpurpose>
+<refname>libsoup Client Basics</refname><refpurpose>Client-side tutorial</refpurpose>
</refnamediv>
<refsect2>
+<para>
+This section explains how to use <application>libsoup</application> as
+an HTTP client using several new APIs introduced in version 2.42. If
+you want to be compatible with older versions of
+<application>libsoup</application>, consult the documentation for that
+version.
+</para>
+</refsect2>
+
+<refsect2>
<title>Creating a <type>SoupSession</type></title>
<para>
@@ -24,35 +34,8 @@ authentication information, etc.
</para>
<para>
-There are two subclasses of <link
-linkend="SoupSession"><type>SoupSession</type></link> that you can use, with
-slightly different behavior:
-</para>
-
-<itemizedlist>
- <listitem><para>
- <link linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>,
- which uses callbacks and the glib main loop to provide
- asynchronous I/O.
- </para></listitem>
-
- <listitem><para>
- <link linkend="SoupSessionSync"><type>SoupSessionSync</type></link>,
- which uses blocking I/O rather than callbacks, making it more
- suitable for threaded applications.
- </para></listitem>
-</itemizedlist>
-
-<para>
-If you want to do a mix of mainloop-based and blocking I/O, you will
-need to create two different session objects.
-</para>
-
-<para>
-When you create the session (with <link
-linkend="soup-session-async-new-with-options"><function>soup_session_async_new_with_options</function></link>
-or <link
-linkend="soup-session-sync-new-with-options"><function>soup_session_sync_new_with_options</function></link>),
+When you create the session with <link
+linkend="soup-session-new-with-options"><function>soup_session_new_with_options</function></link>,
you can specify various additional options:
</para>
@@ -64,7 +47,7 @@ you can specify various additional options:
the session will have open at one time. (Once it reaches
this limit, it will either close idle connections, or
wait for existing connections to free up before starting
- new requests.)
+ new requests.) The default value is 10.
</para></listitem>
</varlistentry>
<varlistentry>
@@ -72,44 +55,61 @@ you can specify various additional options:
<listitem><para>
Allows you to set the maximum total number of connections
the session will have open <emphasis>to a single
- host</emphasis> at one time.
+ host</emphasis> at one time. The default value is 2.
</para></listitem>
</varlistentry>
<varlistentry>
- <term><link linkend="SOUP-SESSION-USE-NTLM:CAPS"><literal>SOUP_SESSION_USE_NTLM</literal></link></term>
+ <term><link linkend="SOUP-SESSION-USER-AGENT:CAPS"><literal>SOUP_SESSION_USER_AGENT</literal></link></term>
<listitem><para>
- If <literal>TRUE</literal>, then Microsoft NTLM
- authentication will be used if available (and will be
- preferred to HTTP Basic or Digest authentication).
- If <literal>FALSE</literal>, NTLM authentication won't be
- used, even if it's the only authentication type available.
- (NTLM works differently from the standard HTTP
- authentication types, so it needs to be handled
- specially.)
+ Allows you to set a User-Agent string that will be sent
+ on all outgoing requests.
</para></listitem>
</varlistentry>
<varlistentry>
- <term><link linkend="SOUP-SESSION-SSL-CA-FILE:CAPS"><literal>SOUP_SESSION_SSL_CA_FILE</literal></link></term>
+ <term><link linkend="SOUP-SESSION-ACCEPT-LANGUAGE:CAPS"><literal>SOUP_SESSION_ACCEPT_LANGUAGE</literal></link>
+ and <link linkend="SOUP-SESSION-ACCEPT-LANGUAGE-AUTO:CAPS"><literal>SOUP_SESSION_ACCEPT_LANGUAGE_AUTO</literal></link></term>
<listitem><para>
- Points to a file containing certificates for recognized
- SSL Certificate Authorities. If this is set, then HTTPS
- connections will be checked against these authorities, and
- rejected if they can't be verified. (Otherwise all SSL
- certificates will be accepted automatically.)
+ Allow you to set an Accept-Language header on all outgoing
+ requests. <literal>SOUP_SESSION_ACCEPT_LANGUAGE</literal>
+ takes a list of language tags to use, while
+ <literal>SOUP_SESSION_ACCEPT_LANGUAGE_AUTO</literal>
+ automatically generates the list from the user's locale
+ settings.
</para></listitem>
</varlistentry>
<varlistentry>
- <term><link linkend="SOUP-SESSION-ASYNC-CONTEXT:CAPS"><literal>SOUP_SESSION_ASYNC_CONTEXT</literal></link></term>
+ <term><link linkend="SOUP-SESSION-HTTP-ALIASES:CAPS"><literal>SOUP_SESSION_HTTP_ALIASES</literal></link>
+ and <link linkend="SOUP-SESSION-HTTPS-ALIASES:CAPS"><literal>SOUP_SESSION_HTTPS_ALIASES</literal></link></term>
<listitem><para>
- A <link
- linkend="GMainContext"><type>GMainContext</type></link>
- which the session will use for asynchronous operations.
- This can be set if you want to use a
- <type>SoupSessionAsync</type> in a thread other than the
- main thread.
+ Allow you to tell the session to recognize additional URI
+ schemes as aliases for "<literal>http</literal>" or
+ <literal>https</literal>. You can set this if you are
+ using URIs with schemes like "<literal>dav</literal>" or
+ "<literal>webcal</literal>" (and in particular, you need
+ to set this if the server you are talking to might return
+ redirects with such a scheme).
</para></listitem>
</varlistentry>
<varlistentry>
+ <term><link linkend="SOUP-SESSION-PROXY-RESOLVER:CAPS"><literal>SOUP_SESSION_PROXY_RESOLVER</literal></link> and <link linkend="SOUP-SESSION-PROXY-URI:CAPS"><literal>SOUP_SESSION_PROXY_URI</literal></link></term>
+ <listitem>
+ <para>
+ <link linkend="SOUP-SESSION-PROXY-RESOLVER:CAPS"><literal>SOUP_SESSION_PROXY_RESOLVER</literal></link>
+ specifies a <link
+ linkend="GProxyResolver"><type>GProxyResolver</type></link>
+ to use to determine the HTTP proxies to use. By default,
+ this is set to the resolver returned by <link
+ linkend="g-proxy-resolver-get-default"><function>g_proxy_resolver_get_default</function></link>,
+ so you do not need to set it yourself.
+ </para>
+ <para>
+ Alternatively, if you want all requests to go through a
+ single proxy, you can set <link
+ linkend="SOUP-SESSION-PROXY-URI:CAPS"><literal>SOUP_SESSION_PROXY_URI</literal></link>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><link linkend="SOUP-SESSION-ADD-FEATURE:CAPS"><literal>SOUP_SESSION_ADD_FEATURE</literal></link> and <link linkend="SOUP-SESSION-ADD-FEATURE-BY-TYPE:CAPS"><literal>SOUP_SESSION_ADD_FEATURE_BY_TYPE</literal></link></term>
<listitem><para>
These allow you to specify <link
@@ -121,10 +121,15 @@ you can specify various additional options:
</variablelist>
<para>
+Other properties are also available; see the <link
+linkend="SoupSession"><type>SoupSession</type></link> documentation for
+more details.
+</para>
+
+<para>
If you don't need to specify any options, you can just use <link
-linkend="soup-session-async-new"><function>soup_session_async_new</function></link> or
-<link linkend="soup-session-sync-new"><function>soup_session_sync_new</function></link>,
-which take no arguments.
+linkend="soup-session-new"><function>soup_session_new</function></link>,
+which takes no arguments.
</para>
</refsect2>
@@ -143,8 +148,19 @@ options at session-construction-time, or afterward via the <link
linkend="soup-session-add-feature"><function>soup_session_add_feature</function></link>
and <link
linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type</function></link>
-functions. Some of the features available in
-<application>libsoup</application> are:
+functions.
+</para>
+
+<para>
+A <link
+linkend="SoupContentDecoder"><type>SoupContentDecoder</type></link> is
+added for you automatically. This advertises to servers that the
+client supports compression, and automatically decompresses compressed
+responses.
+</para>
+
+<para>
+Some other available features that you can add include:
</para>
<variablelist>
@@ -156,33 +172,27 @@ functions. Some of the features available in
</para></listitem>
</varlistentry>
<varlistentry>
- <term><link linkend="SoupCookieJar"><type>SoupCookieJar</type></link> and <link linkend="SoupCookieJarText"><type>SoupCookieJarText</type></link></term>
+ <term>
+ <link linkend="SoupCookieJar"><type>SoupCookieJar</type></link>,
+ <link linkend="SoupCookieJarText"><type>SoupCookieJarText</type></link>,
+ and <link linkend="SoupCookieJarDB"><type>SoupCookieJarDB</type></link>
+ </term>
<listitem><para>
Support for HTTP cookies. <type>SoupCookieJar</type>
provides non-persistent cookie storage, while
<type>SoupCookieJarText</type> uses a text file to keep
- track of cookies between sessions.
- </para></listitem>
- </varlistentry>
-</variablelist>
-
-<para>
-And in <application>libsoup-gnome</application>:
-</para>
-
-<variablelist>
- <varlistentry>
- <term><link linkend="SOUP-TYPE-PROXY-RESOLVER-GNOME:CAPS"><type>SoupProxyResolverGNOME</type></link></term>
- <listitem><para>
- A feature that automatically determines the correct HTTP
- proxy to use for requests.
+ track of cookies between sessions, and
+ <type>SoupCookieJarDB</type> uses a
+ <application>SQLite</application> database.
</para></listitem>
</varlistentry>
<varlistentry>
- <term><link linkend="SoupCookieJarSqlite"><type>SoupCookieJarSqlite</type></link></term>
+ <term><link linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link></term>
<listitem><para>
- Support for HTTP cookies stored in an
- <application>SQLite</application> database.
+ Uses the HTML5 sniffing rules to attempt to
+ determine the Content-Type of a response when the
+ server does not identify the Content-Type, or appears to
+ have provided an incorrect one.
</para></listitem>
</varlistentry>
</variablelist>
@@ -190,7 +200,7 @@ And in <application>libsoup-gnome</application>:
<para>
Use the "add_feature_by_type" property/function to add features that
don't require any configuration (such as <link
-linkend="SOUP-TYPE-PROXY-RESOLVER-GNOME:CAPS"><type>SoupProxyResolverGNOME</type></link>),
+linkend="SoupContentSniffer"><type>SoupContentSniffer</type></link>),
and the "add_feature" property/function to add features that must be
constructed first (such as <link
linkend="SoupLogger"><type>SoupLogger</type></link>). For example, an
@@ -198,11 +208,10 @@ application might do something like the following:
</para>
<informalexample><programlisting>
- session = soup_session_async_new_with_options (
-#ifdef HAVE_LIBSOUP_GNOME
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
-#endif
+ session = soup_session_new_with_options (
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_SNIFFER,
NULL);
+
if (debug_level) {
SoupLogger *logger;
@@ -218,7 +227,7 @@ application might do something like the following:
<title>Creating and Sending SoupMessages</title>
<para>
-Once you have a session, you do HTTP traffic using <link
+Once you have a session, you send HTTP requests using <link
linkend="SoupMessage"><type>SoupMessage</type></link>. In the simplest
case, you only need to create the message and it's ready to send:
</para>
@@ -242,7 +251,7 @@ request headers and body of the message:
msg = soup_message_new ("POST", "http://example.com/form.cgi");
soup_message_set_request (msg, "application/x-www-form-urlencoded",
- SOUP_MEMORY_COPY, formdata, strlen (formdata));
+ SOUP_MEMORY_COPY, formdata, strlen (formdata));
soup_message_headers_append (msg->request_headers, "Referer", referring_url);
</programlisting></informalexample>
@@ -269,28 +278,56 @@ flag.
<para>
To send a message and wait for the response, use <link
-linkend="soup-session-send-message"><function>soup_session_send_message</function></link>:
+linkend="soup-session-send"><function>soup_session_send</function></link>:
</para>
<informalexample><programlisting>
- guint status;
+ GInputStream *stream;
+ GError *error = NULL;
- status = soup_session_send_message (session, msg);
+ stream = soup_session_send (session, msg, cancellable, &amp;error);
</programlisting></informalexample>
<para>
-(If you use <function>soup_session_send_message</function> with a
-<link linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>,
-it will run the main loop itself until the message is complete.)
+At the point when <function>soup_session_send</function> returns, the
+request will have been sent, and the response headers read back in;
+you can examine the message's <structfield>status_code</structfield>,
+<structfield>reason_phrase</structfield>, and
+<structfield>response_headers</structfield> fields to see the response
+metadata. To get the response body, read from the returned <link
+linkend="GInputStream"><type>GInputStream</type></link>, and close it
+when you are done.
</para>
<para>
-The return value from <function>soup_session_send_message</function>
-is a <link linkend="libsoup-2.4-soup-status">libsoup status code</link>,
-indicating either a transport error that prevented the message from
-being sent, or the HTTP status that was returned by the server in
-response to the message. (The status is also available as
-<literal>msg->status_code</literal>.)
+Note that <function>soup_session_send</function> only returns an error
+if a transport-level problem occurs (eg, it could not connect to the
+host, or the request was cancelled). Use the message's
+<structfield>status_code</structfield> field to determine whether the
+request was successful or not at the HTTP level (ie, "<literal>200
+OK</literal>" vs "<literal>401 Bad Request</literal>").
+</para>
+
+<para>
+If you would prefer to have <application>libsoup</application> gather
+the response body for you and then return it all at once, you can use
+the older
+<link linkend="soup-session-send-message"><function>soup_session_send_message</function></link>
+API:
+</para>
+
+<informalexample><programlisting>
+ guint status;
+
+ status = soup_session_send_message (session, msg);
+</programlisting></informalexample>
+
+<para>
+In this case, the response body will be available in the message's
+<structfield>response_body</structfield> field, and transport-level
+errors will be indicated in the <structfield>status_code</structfield>
+field via special pseudo-HTTP-status codes like <link
+linkend="SOUP-STATUS-CANT-CONNECT:CAPS"><literal>SOUP_STATUS_CANT_CONNECT</literal></link>.
</para>
</refsect3>
@@ -300,51 +337,63 @@ response to the message. (The status is also available as
<para>
To send a message asynchronously, use <link
-linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>:
+linkend="soup-session-send-async"><function>soup_session_send_async</function></link>:
</para>
<informalexample><programlisting>
+{
...
- soup_session_queue_message (session, msg, my_callback, my_callback_data);
+ soup_session_send_async (session, msg, cancellable, my_callback, my_callback_data);
...
}
static void
-my_callback (SoupSession *session, SoupMessage *msg, gpointer user_data)
+my_callback (GObject *object, GAsyncResult *result, gpointer user_data)
{
- /* Handle the response here */
+ GInputStream *stream;
+ GError *error = NULL;
+
+ stream = soup_session_send_finish (SOUP_SESSION (object), result, &amp;error);
+ ...
}
</programlisting></informalexample>
<para>
The message will be added to the session's queue, and eventually (when
control is returned back to the main loop), it will be sent and the
-response be will be read. When the message is complete,
-<literal>callback</literal> will be invoked, along with the data you
-passed to <function>soup_session_queue_message</function>.
+response be will be read. When the message has been sent, and its
+headers received, the callback will be invoked, in the standard
+<link linkend="GAsyncReadyCallback"><type>GAsyncReadyCallback</type></link>
+style.
</para>
<para>
-<link
-linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>
-steals a reference to the message object, and unrefs it after the last
-callback is invoked on it. So in the usual case, messages sent
-asynchronously will be automatically freed for you without you needing
-to do anything. (Of course, this wouldn't work when using the synchronous
-API, since you will usually need continue working with the message
-after calling <link
-linkend="soup-session-send-message"><function>soup_session_send_message</function></link>,
-so in that case, you must unref it explicitly when you are done with
-it.)
+As with synchronous sending, there is also an alternate API, <link
+linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>,
+in which your callback is not invoked until the response has been
+completely read:
</para>
+<informalexample><programlisting>
+{
+ ...
+ soup_session_queue_message (session, msg, my_callback, my_callback_data);
+ ...
+}
+
+static void
+my_callback (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ /* msg->response_body contains the response */
+}
+</programlisting></informalexample>
+
<para>
-(If you use <link
+<link
linkend="soup-session-queue-message"><function>soup_session_queue_message</function></link>
-with a <link
-linkend="SoupSessionSync"><type>SoupSessionSync</type></link>, the
-message will be sent in another thread, with the callback eventually
-being invoked in the session's <link linkend="SOUP-SESSION-ASYNC-CONTEXT:CAPS"><literal>SOUP_SESSION_ASYNC_CONTEXT</literal></link>.)
+is slightly unusual in that it steals a reference to the message
+object, and unrefs it after the last callback is invoked on it. So
+when using this API, you should not unref the message yourself.
</para>
</refsect3>
@@ -355,19 +404,17 @@ being invoked in the session's <link linkend="SOUP-SESSION-ASYNC-CONTEXT:CAPS"><
<title>Processing the Response</title>
<para>
-Once you have received the response from the server, synchronously or
-asynchronously, you can look at the response fields in the
-<literal>SoupMessage</literal> to decide what to do next. The
-<structfield>status_code</structfield> and
+Once you have received the initial response from the server,
+synchronously or asynchronously, streaming or not, you can look at the
+response fields in the <literal>SoupMessage</literal> to decide what
+to do next. The <structfield>status_code</structfield> and
<structfield>reason_phrase</structfield> fields contain the numeric
status and textual status response from the server.
<structfield>response_headers</structfield> contains the response
headers, which you can investigate using <link
-linkend="soup-message-headers-get"><function>soup_message_headers_get</function></link> and
-<link
- linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>.
-The response body (if any) is in the
-<structfield>response_body</structfield> field.
+linkend="soup-message-headers-get"><function>soup_message_headers_get</function></link>
+and <link
+linkend="soup-message-headers-foreach"><function>soup_message_headers_foreach</function></link>.
</para>
<para>
@@ -390,41 +437,6 @@ headers much better than functions like
</refsect2>
<refsect2>
-<title>Intermediate/Automatic Processing</title>
-
-<para>
-You can also connect to various <type>SoupMessage</type> signals to do
-processing at intermediate stages of HTTP I/O. Eg, the <link
-linkend="SoupMessage-got-chunk"><literal>got-chunk</literal></link>
-signal is emitted as each piece of the response body is read (allowing
-you to provide progress information when receiving a large response,
-for example). <type>SoupMessage</type> also provides two convenience
-methods, <link
-linkend="soup-message-add-header-handler"><function>soup_message_add_header_handler</function></link>,
-and <link
-linkend="soup-message-add-status-code-handler"><function>soup_message_add_status_code_handler</function></link>,
-which allow you to set up a signal handler that will only be invoked
-for messages with certain response headers or status codes.
-<type>SoupSession</type> uses this internally to handle authentication
-and redirection.
-</para>
-
-<para>
-When using the synchronous API, the callbacks and signal handlers will
-be invoked during the call to <link
-linkend="soup-session-send-message"><function>soup_session_send_message</function></link>.
-</para>
-
-<para>
-To automatically set up handlers on all messages sent via a session,
-you can connect to the session's <link
-linkend="SoupSession-request-started"><literal>request_started</literal></link>
-signal, and add handlers to each message from there.
-</para>
-
-</refsect2>
-
-<refsect2>
<title>Handling Authentication</title>
<para>
@@ -474,38 +486,39 @@ linkend="soup-session-unpause-message"><function>soup_session_unpause_message</f
to resume the paused message.
</para>
+<para>
+By default, NTLM authentication is not enabled. To add NTLM support to
+a session, call:
+</para>
+
+<informalexample><programlisting>
+ soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+</programlisting></informalexample>
+
+<para>
+(You can also disable Basic or Digest authentication by calling <link
+linkend="soup-session-remove-feature-by-type"><function>soup_session_remove_feature_by_type</function></link>
+on <link linkend="SOUP-TYPE-AUTH-BASIC:CAPS"><literal>SOUP_TYPE_AUTH_BASIC</literal></link>
+or <link linkend="SOUP-TYPE-AUTH-DIGEST:CAPS"><literal>SOUP_TYPE_AUTH_DIGEST</literal></link>.)
+</para>
+
</refsect2>
<refsect2>
<title>Multi-threaded usage</title>
<para>
-The only explicitly thread-safe operations in
-<application>libsoup</application> are <link
-linkend="SoupSessionSync"><type>SoupSessionSync</type></link>'s
-implementations of the <link
-linkend="SoupSession"><type>SoupSession</type></link> methods. So
-after creating a <type>SoupSessionSync</type>, you can call <link
-linkend="soup-session-send-message"><function>soup_session_send_message</function></link>
-and <link
-linkend="soup-session-cancel-message"><function>soup_session_cancel_message</function></link>
-on it from any thread. But, eg, while the session is processing a
-message, you should not call any <link
-linkend="SoupMessage"><type>SoupMessage</type></link> methods on it
-from any thread other than the one in which it is being sent. (That
-is, you should not call any <type>SoupMessage</type> methods on it
-except from a message or session callback or signal handler.)
+A <link linkend="SoupSession"><type>SoupSession</type></link> can be
+used from multiple threads. However, if you are using the async APIs,
+then each thread you use the session from must have its own
+thread-default <link linkend="GMainContext"><type>GMainContext</type></link>.
</para>
<para>
-All other objects (including <link
-linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>)
-should only be used from a single thread, with objects that are also
-only be used from that thread. (And in particular, if you set a
-non-default <link
-linkend="GMainContext"><type>GMainContext</type></link> on a session,
-socket, etc, then you can only use that object from the thread in
-which that <type>GMainContext</type> is running.)
+<link linkend="SoupMessage"><type>SoupMessage</type></link> is
+<emphasis>not</emphasis> thread-safe, so once you send a message on
+the session, you must not interact with it from any thread other than
+the one where it was sent.
</para>
</refsect2>
@@ -515,7 +528,8 @@ which that <type>GMainContext</type> is running.)
<para>
A few sample programs are available in the
-<application>libsoup</application> sources:
+<application>libsoup</application> sources, in the
+<literal>examples</literal> directory:
</para>
<itemizedlist>
@@ -525,21 +539,6 @@ A few sample programs are available in the
</para></listitem>
<listitem><para>
- <emphasis role="bold"><literal>getbug</literal></emphasis> is a trivial
- demonstration of the <link
- linkend="libsoup-2.4-XMLRPC-Support">XMLRPC</link> interface.
- (<emphasis
- role="bold"><literal>xmlrpc-test</literal></emphasis> provides
- a slightly more complicated example.)
- </para></listitem>
-
- <listitem><para>
- <emphasis role="bold"><literal>auth-test</literal></emphasis> shows how to use
- authentication handlers and status-code handlers, although in
- a fairly unusual way.
- </para></listitem>
-
- <listitem><para>
<emphasis role="bold"><literal>simple-proxy</literal></emphasis> uses both the
client and server APIs to create a simple (and not very
RFC-compliant) proxy server. It shows how to use the <link
@@ -551,11 +550,7 @@ A few sample programs are available in the
</itemizedlist>
<para>
-More complicated examples are available in GNOME CVS. The <ulink
-url="http://live.gnome.org/LibSoup"><application>libsoup</application>
-pages</ulink> on the GNOME wiki include a <ulink
-url="http://live.gnome.org/LibSoup/Users">list of applications using
-<application>libsoup</application></ulink>.
+More complicated examples are available in GNOME git.
</para>
</refsect2>
diff --git a/docs/reference/libsoup-2.4-docs.sgml b/docs/reference/libsoup-2.4-docs.sgml
index 21e19ea4..c0c8a05c 100644
--- a/docs/reference/libsoup-2.4-docs.sgml
+++ b/docs/reference/libsoup-2.4-docs.sgml
@@ -10,7 +10,9 @@
<title>Tutorial</title>
<xi:include href="build-howto.xml"/>
<xi:include href="client-howto.xml"/>
+ <xi:include href="request-howto.xml"/>
<xi:include href="server-howto.xml"/>
+ <xi:include href="session-porting.xml"/>
</chapter>
<chapter>
@@ -19,6 +21,7 @@
<xi:include href="xml/soup-auth-domain.xml"/>
<xi:include href="xml/soup-auth-domain-basic.xml"/>
<xi:include href="xml/soup-auth-domain-digest.xml"/>
+ <xi:include href="xml/soup-cache.xml"/>
<xi:include href="xml/soup-cookie.xml"/>
<xi:include href="xml/soup-message.xml"/>
<xi:include href="xml/soup-message-headers.xml"/>
@@ -26,6 +29,11 @@
<xi:include href="xml/soup-method.xml"/>
<xi:include href="xml/soup-misc.xml"/>
<xi:include href="xml/soup-multipart.xml"/>
+ <xi:include href="xml/soup-multipart-input-stream.xml"/>
+ <xi:include href="xml/soup-request.xml"/>
+ <xi:include href="xml/soup-request-http.xml"/>
+ <xi:include href="xml/soup-request-file.xml"/>
+ <xi:include href="xml/soup-request-data.xml"/>
<xi:include href="xml/soup-server.xml"/>
<xi:include href="xml/soup-session.xml"/>
<xi:include href="xml/soup-session-async.xml"/>
@@ -33,17 +41,19 @@
<xi:include href="xml/soup-status.xml"/>
<xi:include href="xml/soup-tld.xml"/>
<xi:include href="xml/soup-uri.xml"/>
+ <xi:include href="xml/soup-version.xml"/>
</chapter>
<chapter>
<title>Additional Features</title>
<xi:include href="xml/soup-session-feature.xml"/>
+ <xi:include href="xml/soup-auth-manager.xml"/>
<xi:include href="xml/soup-content-decoder.xml"/>
<xi:include href="xml/soup-content-sniffer.xml"/>
<xi:include href="xml/soup-cookie-jar.xml"/>
<xi:include href="xml/soup-cookie-jar-text.xml"/>
+ <xi:include href="xml/soup-cookie-jar-db.xml"/>
<xi:include href="xml/soup-logger.xml"/>
- <xi:include href="xml/soup-proxy-uri-resolver.xml"/>
<xi:include href="xml/soup-proxy-resolver-default.xml"/>
</chapter>
@@ -55,27 +65,11 @@
</chapter>
<chapter>
- <title>GNOME integration</title>
- <xi:include href="xml/soup-gnome-features.xml"/>
- <xi:include href="xml/soup-cookie-jar-sqlite.xml"/>
- </chapter>
-
- <chapter>
<title>Low-level Networking API</title>
<xi:include href="xml/soup-address.xml"/>
<xi:include href="xml/soup-socket.xml"/>
</chapter>
- <chapter>
- <title>Experimental streaming API</title>
- <xi:include href="xml/soup-requester.xml"/>
- <xi:include href="xml/soup-request.xml"/>
- <xi:include href="xml/soup-request-http.xml"/>
- <xi:include href="xml/soup-request-file.xml"/>
- <xi:include href="xml/soup-request-data.xml"/>
- <xi:include href="xml/soup-cache.xml"/>
- </chapter>
-
<index>
<title>Index</title>
</index>
diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt
index 39e8288a..03d35d4b 100644
--- a/docs/reference/libsoup-2.4-sections.txt
+++ b/docs/reference/libsoup-2.4-sections.txt
@@ -35,6 +35,11 @@ SoupChunkAllocator
soup_message_set_chunk_allocator
<SUBSECTION>
soup_message_disable_feature
+soup_message_get_soup_request
+<SUBSECTION>
+SoupMessagePriority
+soup_message_get_priority
+soup_message_set_priority
<SUBSECTION>
SOUP_MESSAGE_METHOD
SOUP_MESSAGE_URI
@@ -44,9 +49,12 @@ SOUP_MESSAGE_STATUS_CODE
SOUP_MESSAGE_REASON_PHRASE
SOUP_MESSAGE_SERVER_SIDE
SOUP_MESSAGE_FIRST_PARTY
+SOUP_MESSAGE_PRIORITY
SOUP_MESSAGE_REQUEST_BODY
+SOUP_MESSAGE_REQUEST_BODY_DATA
SOUP_MESSAGE_REQUEST_HEADERS
SOUP_MESSAGE_RESPONSE_BODY
+SOUP_MESSAGE_RESPONSE_BODY_DATA
SOUP_MESSAGE_RESPONSE_HEADERS
SOUP_MESSAGE_TLS_CERTIFICATE
SOUP_MESSAGE_TLS_ERRORS
@@ -193,13 +201,14 @@ SOUP_STATUS_IS_SUCCESSFUL
SOUP_STATUS_IS_REDIRECTION
SOUP_STATUS_IS_CLIENT_ERROR
SOUP_STATUS_IS_SERVER_ERROR
-SoupKnownStatusCode
+SoupStatus
soup_status_get_phrase
soup_status_proxify
<SUBSECTION>
SOUP_HTTP_ERROR
<SUBSECTION Private>
soup_http_error_quark
+SoupKnownStatusCode
</SECTION>
<SECTION>
@@ -241,6 +250,8 @@ SOUP_SERVER_TLS_CERTIFICATE
SOUP_SERVER_ASYNC_CONTEXT
SOUP_SERVER_RAW_PATHS
SOUP_SERVER_SERVER_HEADER
+SOUP_SERVER_HTTP_ALIASES
+SOUP_SERVER_HTTPS_ALIASES
<SUBSECTION Standard>
SOUP_SERVER
SOUP_IS_SERVER
@@ -370,6 +381,7 @@ SOUP_ADDRESS_NAME
SOUP_ADDRESS_PHYSICAL
SOUP_ADDRESS_PORT
SOUP_ADDRESS_SOCKADDR
+SOUP_ADDRESS_PROTOCOL
<SUBSECTION Standard>
SOUP_ADDRESS
SOUP_IS_ADDRESS
@@ -386,11 +398,26 @@ SoupAddressClass
<TITLE>SoupSession</TITLE>
SoupSession
<SUBSECTION>
+soup_session_new
+soup_session_new_with_options
+<SUBSECTION>
+soup_session_request
+soup_session_request_uri
+soup_session_request_http
+soup_session_request_http_uri
+SoupRequestError
+SOUP_REQUEST_ERROR
+<SUBSECTION>
SoupSessionCallback
soup_session_queue_message
soup_session_requeue_message
soup_session_send_message
soup_session_cancel_message
+<SUBSECTION>
+soup_session_send
+soup_session_send_async
+soup_session_send_finish
+<SUBSECTION>
soup_session_prefetch_dns
soup_session_prepare_for_uri
soup_session_abort
@@ -410,15 +437,18 @@ soup_session_remove_feature_by_type
soup_session_get_features
soup_session_get_feature
soup_session_get_feature_for_message
+soup_session_has_feature
<SUBSECTION>
SOUP_SESSION_PROXY_URI
+SOUP_SESSION_PROXY_RESOLVER
SOUP_SESSION_MAX_CONNS
SOUP_SESSION_MAX_CONNS_PER_HOST
-SOUP_SESSION_USE_NTLM
-SOUP_SESSION_SSL_CA_FILE
-SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE
SOUP_SESSION_TLS_DATABASE
+SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE
+SOUP_SESSION_SSL_CA_FILE
+SOUP_SESSION_SSL_STRICT
SOUP_SESSION_ASYNC_CONTEXT
+SOUP_SESSION_USE_THREAD_CONTEXT
SOUP_SESSION_TIMEOUT
SOUP_SESSION_IDLE_TIMEOUT
SOUP_SESSION_USER_AGENT
@@ -427,10 +457,9 @@ SOUP_SESSION_ADD_FEATURE_BY_TYPE
SOUP_SESSION_REMOVE_FEATURE_BY_TYPE
SOUP_SESSION_ACCEPT_LANGUAGE
SOUP_SESSION_ACCEPT_LANGUAGE_AUTO
-SOUP_SESSION_SSL_STRICT
SOUP_SESSION_HTTP_ALIASES
SOUP_SESSION_HTTPS_ALIASES
-SOUP_SESSION_USE_THREAD_CONTEXT
+SOUP_SESSION_LOCAL_ADDRESS
<SUBSECTION Standard>
SOUP_IS_SESSION
SOUP_IS_SESSION_CLASS
@@ -440,11 +469,13 @@ SOUP_SESSION_GET_CLASS
SOUP_TYPE_SESSION
SoupSessionClass
soup_session_get_type
+soup_request_error_quark
<SUBSECTION Private>
SoupConnection
SoupConnectionState
SoupMessageQueue
SoupMessageQueueItem
+SOUP_SESSION_USE_NTLM
</SECTION>
<SECTION>
@@ -521,6 +552,7 @@ soup_auth_get_info
<SUBSECTION>
soup_auth_authenticate
soup_auth_is_authenticated
+soup_auth_is_ready
<SUBSECTION>
soup_auth_get_authorization
soup_auth_get_protection_space
@@ -551,6 +583,23 @@ soup_auth_save_password
</SECTION>
<SECTION>
+<FILE>soup-auth-manager</FILE>
+<TITLE>SoupAuthManager</TITLE>
+SoupAuthManager
+SOUP_TYPE_AUTH_MANAGER
+soup_auth_manager_use_auth
+<SUBSECTION Standard>
+SoupAuthManagerPrivate
+SoupAuthManagerClass
+SOUP_AUTH_MANAGER
+SOUP_IS_AUTH_MANAGER
+SOUP_AUTH_MANAGER_CLASS
+SOUP_IS_AUTH_MANAGER_CLASS
+SOUP_AUTH_MANAGER_GET_CLASS
+soup_auth_manager_get_type
+</SECTION>
+
+<SECTION>
<FILE>soup-socket</FILE>
<TITLE>SoupSocket</TITLE>
SoupSocket
@@ -571,6 +620,7 @@ soup_socket_is_connected
<SUBSECTION>
soup_socket_get_local_address
soup_socket_get_remote_address
+soup_socket_get_fd
<SUBSECTION>
SoupSocketIOStatus
soup_socket_read
@@ -599,12 +649,6 @@ SOUP_SOCKET_CLASS
SOUP_IS_SOCKET_CLASS
SOUP_SOCKET_GET_CLASS
SoupSocketClass
-<SUBSECTION Private>
-soup_socket_get_fd
-soup_socket_handshake_async
-soup_socket_handshake_sync
-soup_socket_get_gsocket
-soup_socket_get_iostream
</SECTION>
<SECTION>
@@ -631,6 +675,7 @@ SOUP_URI_SCHEME_HTTPS
SOUP_URI_SCHEME_DATA
SOUP_URI_SCHEME_FILE
SOUP_URI_SCHEME_FTP
+SOUP_URI_SCHEME_RESOURCE
soup_uri_uses_default_port
SOUP_URI_IS_VALID
SOUP_URI_VALID_FOR_HTTP
@@ -656,8 +701,6 @@ soup_uri_get_fragment
<SUBSECTION Standard>
SOUP_TYPE_URI
soup_uri_get_type
-<SUBSECTION Private>
-uri_decoded_copy
</SECTION>
<SECTION>
@@ -705,8 +748,6 @@ soup_add_completion
soup_add_idle
soup_add_io_watch
soup_add_timeout
-<SUBSECTION>
-soup_ssl_supported
<SUBSECTION Private>
soup_date_copy
SOUP_TYPE_DATE
@@ -721,6 +762,7 @@ SOUP_CHAR_HTTP_SEPARATOR
SOUP_CHAR_URI_GEN_DELIMS
SOUP_CHAR_URI_PERCENT_ENCODED
SOUP_CHAR_URI_SUB_DELIMS
+soup_ssl_supported
</SECTION>
<SECTION>
@@ -927,6 +969,27 @@ soup_multipart_get_type
</SECTION>
<SECTION>
+<FILE>soup-multipart-input-stream</FILE>
+<TITLE>SoupMultipartInputStream</TITLE>
+SoupMultipartInputStream
+soup_multipart_input_stream_new
+<SUBSECTION>
+soup_multipart_input_stream_get_headers
+soup_multipart_input_stream_next_part
+soup_multipart_input_stream_next_part_async
+soup_multipart_input_stream_next_part_finish
+<SUBSECTION Standard>
+SOUP_IS_MULTIPART_INPUT_STREAM
+SOUP_IS_MULTIPART_INPUT_STREAM_CLASS
+SOUP_MULTIPART_INPUT_STREAM
+SOUP_MULTIPART_INPUT_STREAM_CLASS
+SOUP_MULTIPART_INPUT_STREAM_GET_CLASS
+SOUP_TYPE_MULTIPART_INPUT_STREAM
+soup_multipart_input_stream_get_type
+SoupMultipartInputStreamPrivate
+</SECTION>
+
+<SECTION>
<FILE>soup-cookie-jar-text</FILE>
<TITLE>SoupCookieJarText</TITLE>
SoupCookieJarText
@@ -945,52 +1008,21 @@ soup_cookie_jar_text_get_type
</SECTION>
<SECTION>
-<INCLUDE>libsoup/soup-gnome.h</INCLUDE>
-<FILE>soup-cookie-jar-sqlite</FILE>
-<TITLE>SoupCookieJarSqlite</TITLE>
-SoupCookieJarSqlite
-soup_cookie_jar_sqlite_new
+<FILE>soup-cookie-jar-db</FILE>
+<TITLE>SoupCookieJarDB</TITLE>
+SoupCookieJarDB
+soup_cookie_jar_db_new
<SUBSECTION>
-SOUP_COOKIE_JAR_SQLITE_FILENAME
-<SUBSECTION Standard>
-SoupCookieJarSqliteClass
-SOUP_COOKIE_JAR_SQLITE
-SOUP_COOKIE_JAR_SQLITE_CLASS
-SOUP_COOKIE_JAR_SQLITE_GET_CLASS
-SOUP_TYPE_COOKIE_JAR_SQLITE
-SOUP_IS_COOKIE_JAR_SQLITE
-SOUP_IS_COOKIE_JAR_SQLITE_CLASS
-soup_cookie_jar_sqlite_get_type
-</SECTION>
-
-<SECTION>
-<FILE>soup-proxy-uri-resolver</FILE>
-<TITLE>SoupProxyURIResolver</TITLE>
-SoupProxyURIResolver
-SoupProxyURIResolverCallback
-soup_proxy_uri_resolver_get_proxy_uri_async
-soup_proxy_uri_resolver_get_proxy_uri_sync
-<SUBSECTION Standard>
-SoupProxyURIResolverInterface
-SOUP_IS_PROXY_URI_RESOLVER
-SOUP_IS_PROXY_URI_RESOLVER_CLASS
-SOUP_PROXY_URI_RESOLVER
-SOUP_PROXY_URI_RESOLVER_CLASS
-SOUP_PROXY_URI_RESOLVER_GET_CLASS
-SOUP_TYPE_PROXY_URI_RESOLVER
-soup_proxy_uri_resolver_get_type
-</SECTION>
-
-<SECTION>
-<INCLUDE>libsoup/soup-gnome.h</INCLUDE>
-<FILE>soup-gnome-features</FILE>
-SOUP_TYPE_GNOME_FEATURES_2_26
-SOUP_TYPE_PROXY_RESOLVER_GNOME
+SOUP_COOKIE_JAR_DB_FILENAME
<SUBSECTION Standard>
-soup_proxy_resolver_gnome_get_type
-soup_gnome_features_2_26_get_type
-SOUP_TYPE_PASSWORD_MANAGER_GNOME
-soup_password_manager_gnome_get_type
+SoupCookieJarDBClass
+SOUP_COOKIE_JAR_DB
+SOUP_COOKIE_JAR_DB_CLASS
+SOUP_COOKIE_JAR_DB_GET_CLASS
+SOUP_TYPE_COOKIE_JAR_DB
+SOUP_IS_COOKIE_JAR_DB
+SOUP_IS_COOKIE_JAR_DB_CLASS
+soup_cookie_jar_db_get_type
</SECTION>
<SECTION>
@@ -999,6 +1031,7 @@ soup_password_manager_gnome_get_type
SoupContentSniffer
soup_content_sniffer_new
soup_content_sniffer_sniff
+soup_content_sniffer_get_buffer_size
<SUBSECTION Standard>
SOUP_CONTENT_SNIFFER
SOUP_CONTENT_SNIFFER_CLASS
@@ -1033,11 +1066,7 @@ SOUP_CACHE_GET_CLASS
SoupCacheClass
SoupCachePrivate
<SUBSECTION Private>
-soup_cache_generate_conditional_request
-soup_cache_get_cacheability
soup_cache_get_type
-soup_cache_has_response
-soup_cache_send_response
SoupCacheResponse
SoupCacheability
</SECTION>
@@ -1074,30 +1103,6 @@ SOUP_IS_PROXY_RESOLVER_DEFAULT_CLASS
</SECTION>
<SECTION>
-<FILE>soup-requester</FILE>
-<TITLE>SoupRequester</TITLE>
-SoupRequester
-soup_requester_new
-soup_requester_request
-soup_requester_request_uri
-<SUBSECTION>
-SoupRequesterError
-SOUP_REQUESTER_ERROR
-<SUBSECTION Standard>
-soup_requester_get_type
-SoupRequesterClass
-SoupRequesterPrivate
-SOUP_TYPE_REQUESTER
-SOUP_REQUESTER
-SOUP_REQUESTER_CLASS
-SOUP_REQUESTER_GET_CLASS
-SOUP_IS_REQUESTER
-SOUP_IS_REQUESTER_CLASS
-<SUBSECTION Private>
-soup_requester_error_quark
-</SECTION>
-
-<SECTION>
<FILE>soup-request</FILE>
<TITLE>SoupRequest</TITLE>
SoupRequest
@@ -1186,3 +1191,72 @@ SoupTLDError
<SUBSECTION Private>
soup_tld_error_quark
</SECTION>
+
+<SECTION>
+<FILE>soup-version</FILE>
+<TITLE>Version Information</TITLE>
+soup_get_major_version
+soup_get_minor_version
+soup_get_micro_version
+soup_check_version
+<SUBSECTION>
+SOUP_MAJOR_VERSION
+SOUP_MINOR_VERSION
+SOUP_MICRO_VERSION
+SOUP_CHECK_VERSION
+<SUBSECTION>
+SOUP_VERSION_MIN_REQUIRED
+SOUP_VERSION_MAX_ALLOWED
+SOUP_VERSION_2_24
+SOUP_VERSION_2_26
+SOUP_VERSION_2_28
+SOUP_VERSION_2_30
+SOUP_VERSION_2_32
+SOUP_VERSION_2_34
+SOUP_VERSION_2_36
+SOUP_VERSION_2_38
+SOUP_VERSION_2_40
+SOUP_VERSION_2_42
+SOUP_VERSION_2_44
+SOUP_VERSION_2_46
+<SUBSECTION Private>
+SOUP_AVAILABLE_IN_2_24
+SOUP_AVAILABLE_IN_2_26
+SOUP_AVAILABLE_IN_2_28
+SOUP_AVAILABLE_IN_2_30
+SOUP_AVAILABLE_IN_2_32
+SOUP_AVAILABLE_IN_2_34
+SOUP_AVAILABLE_IN_2_36
+SOUP_AVAILABLE_IN_2_38
+SOUP_AVAILABLE_IN_2_40
+SOUP_AVAILABLE_IN_2_42
+SOUP_AVAILABLE_IN_2_44
+SOUP_AVAILABLE_IN_2_46
+SOUP_DEPRECATED_IN_2_24
+SOUP_DEPRECATED_IN_2_24_FOR
+SOUP_DEPRECATED_IN_2_26
+SOUP_DEPRECATED_IN_2_26_FOR
+SOUP_DEPRECATED_IN_2_28
+SOUP_DEPRECATED_IN_2_28_FOR
+SOUP_DEPRECATED_IN_2_30
+SOUP_DEPRECATED_IN_2_30_FOR
+SOUP_DEPRECATED_IN_2_32
+SOUP_DEPRECATED_IN_2_32_FOR
+SOUP_DEPRECATED_IN_2_34
+SOUP_DEPRECATED_IN_2_34_FOR
+SOUP_DEPRECATED_IN_2_36
+SOUP_DEPRECATED_IN_2_36_FOR
+SOUP_DEPRECATED_IN_2_38
+SOUP_DEPRECATED_IN_2_38_FOR
+SOUP_DEPRECATED_IN_2_40
+SOUP_DEPRECATED_IN_2_40_FOR
+SOUP_DEPRECATED_IN_2_42
+SOUP_DEPRECATED_IN_2_42_FOR
+SOUP_DEPRECATED_IN_2_44
+SOUP_DEPRECATED_IN_2_44_FOR
+SOUP_DEPRECATED_IN_2_46
+SOUP_DEPRECATED_IN_2_46_FOR
+SOUP_ENCODE_VERSION
+SOUP_VERSION_CUR_STABLE
+SOUP_VERSION_PREV_STABLE
+</SECTION>
diff --git a/docs/reference/request-howto.xml b/docs/reference/request-howto.xml
new file mode 100644
index 00000000..55a46431
--- /dev/null
+++ b/docs/reference/request-howto.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<refentry id="libsoup-request-howto">
+<refmeta>
+<refentrytitle>libsoup Client SoupRequest API</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBSOUP Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>libsoup Client SoupRequest API</refname><refpurpose>Using
+libsoup with a mix of <literal>http</literal> and non-<literal>http</literal> URIs.</refpurpose>
+</refnamediv>
+
+<refsect2>
+<title><type>SoupRequest</type></title>
+
+<para>
+<link linkend="SoupRequest"><type>SoupRequest</type></link> is an
+abstract type representing a request for a particular URI. The
+<type>SoupRequest</type> API is an alternative to the <link
+linkend="SoupMessage"><type>SoupMessage</type></link>-based <link
+linkend="SoupSession"><type>SoupSession</type></link> APIs which may be
+useful to programs that want to deal with multiple kinds of URIs.
+</para>
+
+<para>
+<type>SoupRequest</type> officially became part of the
+<application>libsoup</application> API in 2.42 with the addition of
+<link
+linkend="soup-session-request"><function>soup_session_request</function></link>
+and the related functions. However, parts of it are also available as
+far back as <application>libsoup</application> 2.34 via the
+(now-deprecated) <type>SoupRequester</type> session feature, if you
+define <literal>LIBSOUP_USE_UNSTABLE_REQUEST_API</literal> before
+including the <application>libsoup</application> headers.
+</para>
+
+<para>
+Additionally, before <application>libsoup</application> 2.42, the
+<type>SoupRequest</type> API was the only way to stream an HTTP
+response body via <link
+linkend="GInputStream"><type>GInputStream</type></link>. As of 2.42,
+there are streaming APIs based on <type>SoupMessage</type> (<link
+linkend="soup-session-send"><function>soup_session_send</function></link>
+and <link
+linkend="soup-session-send-async"><function>soup_session_send_async</function></link>),
+so applications that are using <type>SoupRequest</type> with only
+<literal>http</literal> and <literal>https</literal> URIs can be
+ported to those APIs now.
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Creating a SoupRequest</title>
+
+<para>
+There are four <type>SoupSession</type> methods for creating
+<type>SoupRequest</type>s:
+</para>
+
+<itemizedlist>
+ <listitem>
+ <para>
+ <link linkend="soup-session-request"><function>soup_session_request</function></link>
+ takes an arbitrary URI as a string, and returns a <link
+ linkend="SoupRequest"><type>SoupRequest</type></link>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <link linkend="soup-session-request-uri"><function>soup_session_request_uri</function></link>
+ takes an arbitrary URI as a <link linkend="SoupURI"><type>SoupURI</type></link>,
+ and returns a <link linkend="SoupRequest"><type>SoupRequest</type></link>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <link linkend="soup-session-request-http"><function>soup_session_request_http</function></link>
+ takes an HTTP method and an <literal>http</literal> or <literal>https</literal> URI as a string, and returns a <link
+ linkend="SoupRequestHTTP"><type>SoupRequestHTTP</type></link>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <link linkend="soup-session-request-http-uri"><function>soup_session_request_http_uri</function></link>
+ takes an HTTP method and an <literal>http</literal> or <literal>https</literal> URI as a <link linkend="SoupURI"><type>SoupURI</type></link>,
+ and returns a <link linkend="SoupRequestHTTP"><type>SoupRequestHTTP</type></link>.
+ </para>
+ </listitem>
+</itemizedlist>
+
+</refsect2>
+
+<refsect2>
+<title>Sending a SoupRequest</title>
+
+<para>
+Once you have created a <type>SoupRequest</type>, you can send it with
+either <link
+linkend="soup-request-send"><function>soup_request_send</function></link>
+or <link
+linkend="soup-request-send-async"><function>soup_request_send_async</function></link>.
+This will provide you with a <link
+linkend="GInputStream"><type>GInputStream</type></link> which you can
+read to get the response body.
+</para>
+
+<para>
+After sending, you can use <link
+linkend="soup-request-get-content-length"><function>soup_request_get_content_length</function></link>
+and <link
+linkend="soup-request-get-content-type"><function>soup_request_get_content_type</function></link>
+to get information about the response body.
+</para>
+
+<para>
+As with the streaming <type>SoupMessage</type>-based APIs,
+<function>soup_request_send</function> and
+<function>soup_request_send_async</function> only return errors if a
+transport-level problem occurs (eg, it could not connect to the host,
+or the request was cancelled). In the case of an HTTP request, use the
+message's <structfield>status_code</structfield> field to determine
+whether the request was successful or not at the HTTP level (ie, "<literal>200
+OK</literal>" vs "<literal>401 Bad Request</literal>"). (You can call <link
+linkend="soup-request-http-get-message"><function>soup_request_http_get_message</function></link>
+to get the request's corresponding <link
+linkend="SoupMessage"><type>SoupMessage</type></link>, to look at the
+status code or other HTTP metadata.)
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Supported URI types, and adding your own</title>
+
+<para>
+Different URI types are implemented by different subclasses of
+<type>SoupRequest</type>. <application>libsoup</application> currently
+implements three <type>SoupRequest</type> classes:
+</para>
+
+<variablelist>
+ <varlistentry>
+ <term><link linkend="SoupRequestHTTP"><type>SoupRequestHTTP</type></link></term>
+ <listitem><para>
+ Handles <literal>http</literal> and
+ <literal>https</literal> URI.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SoupRequestData"><type>SoupRequestData</type></link></term>
+ <listitem><para>
+ Handles <literal>data</literal> URIs containing inline data.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><link linkend="SoupRequestFile"><type>SoupRequestFile</type></link></term>
+ <listitem><para>
+ Handles <literal>file</literal> and
+ <link linkend="GResource"><literal>resource</literal></link> URIs.
+ If you request a URI corresponding to a directory, this
+ will generate an HTML listing of the directory.
+ </para></listitem>
+ </varlistentry>
+</variablelist>
+
+<para>
+You can add additional URI types by implementing your own
+<type>SoupRequest</type> subclass; set the
+<type>SoupRequestClass</type>'s <structfield>schemes</structfield>
+field to point to a <literal>NULL</literal>-terminated array of scheme
+names, implement the various <type>SoupRequest</type> methods, and
+then register the type with your <type>SoupSession</type> by calling
+<link linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type</function></link>
+and passing the <link linkend="GType"><type>GType</type></link> of
+your request class.
+</para>
+
+</refsect2>
+
+</refentry>
diff --git a/docs/reference/server-howto.xml b/docs/reference/server-howto.xml
index 76c19182..05fb0c05 100644
--- a/docs/reference/server-howto.xml
+++ b/docs/reference/server-howto.xml
@@ -100,7 +100,7 @@ to set a callback to handle certain URI paths.
<informalexample><programlisting>
soup_server_add_handler (server, "/foo", server_callback,
- data, destroy_notify);
+ data, destroy_notify);
</programlisting></informalexample>
<para>
@@ -131,11 +131,11 @@ A handler callback looks something like this:
<informalexample><programlisting>
static void
server_callback (SoupServer *server,
- SoupMessage *msg,
- const char *path,
- GHashTable *query,
- SoupClientContext *client,
- gpointer user_data)
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
{
...
}
@@ -192,11 +192,11 @@ data available at once.
<informalexample><programlisting>
static void
server_callback (SoupServer *server,
- SoupMessage *msg,
- const char *path,
- GHashTable *query,
- SoupClientContext *client,
- gpointer user_data)
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
{
MyServerData *server_data = user_data;
const char *mime_type;
@@ -219,7 +219,7 @@ server_callback (SoupServer *server,
soup_message_set_status (msg, SOUP_STATUS_OK);
soup_message_set_response (msg, mime_type, SOUP_MEMORY_COPY,
- body->data, body->len);
+ body->data, body->len);
}
</programlisting></informalexample>
@@ -264,7 +264,7 @@ is emitted indicating that the previous one was written successfully.)
<para>
The <emphasis role="bold"><literal>simple-proxy</literal></emphasis>
-example in the <literal>tests/</literal> directory gives an example of
+example in the <literal>examples/</literal> directory gives an example of
using <literal>chunked</literal> encoding.
</para>
@@ -308,8 +308,8 @@ passed to the <literal>server_callback</literal>:
<informalexample><programlisting>
static gboolean
auth_callback (SoupAuthDomain *domain, SoupMessage *msg,
- const char *username, const char *password,
- gpointer user_data)
+ const char *username, const char *password,
+ gpointer user_data)
{
MyServerData *server_data = user_data;
MyUserData *user;
@@ -360,8 +360,8 @@ domains.
<para>
If you want to require authentication for some requests under a
certain path, but not all of them (eg, you want to authenticate
-<literal>PUT</literal>s, but not <literal>GET</literal>s), use a
-<link
+<literal>PUT</literal> requests, but not <literal>GET</literal>
+requests), use a <link
linkend="SoupAuthDomainFilter"><type>SoupAuthDomainFilter</type></link>.
</para>
diff --git a/docs/reference/session-porting.xml b/docs/reference/session-porting.xml
new file mode 100644
index 00000000..67a433a1
--- /dev/null
+++ b/docs/reference/session-porting.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<refentry id="libsoup-session-porting">
+<refmeta>
+<refentrytitle>Porting to the new SoupSession</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBSOUP Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Porting to the new SoupSession</refname><refpurpose>Notes on
+porting from SoupSessionAsync and SoupSessionSync to SoupSession</refpurpose>
+</refnamediv>
+
+<refsect2 id="intro">
+<title>Introduction</title>
+
+<para>
+As of libsoup 2.42, <link
+linkend="SoupSession"><type>SoupSession</type></link> is no longer an
+abstract class, and the base <type>SoupSession</type> class is now
+preferred over its traditional subclasses, <link
+linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link> and
+<link linkend="SoupSessionSync"><type>SoupSessionSync</type></link>.
+</para>
+
+<para>
+There are several changes in behavior between the old and new sessions
+to be aware of.
+</para>
+
+</refsect2>
+
+<refsect2 id="defaults">
+<title>Different defaults</title>
+
+<para>
+The new <link linkend="SoupSession"><type>SoupSession</type></link>
+has different (and hopefully better) defaults than <link
+linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link> and
+<link linkend="SoupSessionSync"><type>SoupSessionSync</type></link>:
+</para>
+
+<itemizedlist>
+ <listitem>
+ <para>
+ The system TLS/SSL certificate database is used by default to
+ validate https certificates, and sites with invalid certificates
+ will refuse to load with a
+ <link linkend="SOUP-STATUS-SSL-FAILED:CAPS"><literal>SOUP_STATUS_SSL_FAILED</literal></link>
+ error.
+ </para>
+ <para>
+ You can still override the CA database as before, by setting the
+ <link linkend="SoupSession--ssl-ca-file"><type>"ssl-ca-file"</type></link>
+ property, although the
+ <link linkend="SoupSession--tls-database"><type>"tls-database"</type></link>
+ property is preferred, since it allows you to do proper error
+ handling.
+ </para>
+ <para>
+ If you want to accept all certificates, set
+ <link linkend="SoupSession--ssl-strict"><type>"ssl-strict"</type></link> to
+ <literal>FALSE</literal>. Note that libsoup will still check
+ certificates, it will just continue with the HTTP request even
+ if the certificate fails to validate. You can use
+ <link linkend="soup-message-get-https-status"><function>soup_message_get_https_status()</function></link>
+ to look at the certificate after the fact.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The
+ <link linkend="SoupSession--timeout"><type>"timeout"</type></link>
+ and
+ <link linkend="SoupSession--idle-timeout"><type>"idle-timeout"</type></link>
+ properties both default to 60 seconds.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The
+ <link linkend="SoupSession--http-aliases"><type>"http-aliases"</type></link>
+ property defaults to <literal>NULL</literal>, meaning that URI
+ schemes like "<literal>webcal</literal>" and
+ "<literal>dav</literal>" (and "<literal>ftp</literal>") are not
+ considered to be aliases for "<literal>http</literal>", and so
+ libsoup will not accept requests for such URIs, and will not
+ follow redirects to such URIs.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The new
+ <link linkend="SoupSession--proxy-resolver"><type>"proxy-resolver"</type></link>
+ property is now initialized to the default
+ <link linkend="GProxyResolver"><type>GProxyResolver</type></link>,
+ meaning that it will automatically use the user's system proxy
+ configuration. This replaces the use of the
+ <link linkend="SoupProxyResolverDefault"><type>SoupProxyResolverDefault</type></link>,
+ session feature in earlier releases. You can set this property to
+ <literal>NULL</literal> if you don't want to use proxies, and the
+ <link linkend="SoupSession--proxy-uri"><type>"proxy-uri"</type></link>
+ property still works if you want to use a single proxy for all requests.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Every session gets a
+ <link linkend="SoupContentDecoder"><type>SoupContentDecoder</type></link>
+ attached to it by default, meaning that it will automatically
+ handle (and request) "gzip"- and "deflate"-encoded response
+ bodies.
+ </para>
+ </listitem>
+</itemizedlist>
+
+</refsect2>
+
+<refsect2 id="behavior">
+<title>Differences in feature behavior</title>
+
+<para>
+If you are using NTLM authentication, the new <type>SoupSession</type>
+behaves slightly differently from the old session types.
+</para>
+
+<para>
+First, the deprecated <link
+linkend="SOUP-SESSION-USE-NTLM:CAPS"><literal>SOUP_SESSION_USE_NTLM</literal></link>
+property is no longer supported. If you want to add support for NTLM
+to a session, call <link
+linkend="soup-session-add-feature-by-type"><function>soup_session_add_feature_by_type()</function></link>,
+passing <link
+linkend="SOUP-TYPE-AUTH-NTLM:CAPS"><literal>SOUP_TYPE_AUTH_NTLM</literal></link>.
+</para>
+
+<para>
+Second, with the old session types, enabling NTLM would cause all
+(otherwise-unauthenticated) requests to be sent with an NTLM request
+in the <literal>Authorization</literal> header. That is, libsoup would
+assume that all servers supported NTLM, and would attempt to begin
+negotiating NTLM authentication before the server ever returned a 401
+response. With the plain <type>SoupSession</type>, this no longer
+happens. If you want the old behavior, you need to call <link
+linkend="soup-auth-manager-use-auth"><function>soup_auth_manager_use_auth()</function></link>
+for each host to "preload" the NTLM authentication:
+</para>
+
+<informalexample><programlisting>
+ SoupAuthManager *auth_manager;
+ SoupAuth *auth;
+ SoupURI *uri;
+
+ auth_manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER));
+ auth = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL);
+ uri = soup_uri_new ("http://ntlm-using-host.example.com/");
+ soup_auth_manager_use_auth (auth_manager, uri, auth);
+ g_object_unref (auth);
+ soup_uri_free (auth);
+</programlisting></informalexample>
+
+</refsect2>
+
+<refsect2 id="apis">
+<title>Differences in SoupMessage-sending APIs</title>
+
+<para>
+<type>SoupSessionAsync</type> always uses asynchronous I/O, and
+<type>SoupSessionSync</type> always uses blocking I/O, regardless of
+the operation. In the new <type>SoupSession</type>, <link
+linkend="soup-session-queue-message"><function>soup_session_queue_message()</function></link>
+uses asynchronous I/O (like <type>SoupSessionAsync</type>), and <link
+linkend="soup-session-send-message"><function>soup_session_send_message()</function></link>
+uses blocking I/O (like <type>SoupSessionSync</type>). There is no API
+on the plain <type>SoupSession</type> that simulates the effect of
+calling <function>soup_session_send_message()</function> on a
+<type>SoupSessionAsync</type> (ie, running the main loop internally),
+or of calling <function>soup_session_queue_message()</function> on a
+<type>SoupSessionSync</type> (ie, automatically sending the request in
+another thread).
+</para>
+
+</refsect2>
+
+<refsect2 id="async">
+<title>Differences in Asynchronous I/O</title>
+
+<para>
+As compared to <link
+linkend="SoupSessionAsync"><type>SoupSessionAsync</type></link>, <link
+linkend="SoupSession"><type>SoupSession</type></link> behaves more
+like gio with respect to asynchronous I/O.
+</para>
+
+<para>
+In particular, the <link
+linkend="SoupSession--async-context"><type>"async-context"</type></link>
+and <link
+linkend="SoupSession--use-thread-context"><type>"use-thread-context"</type></link>
+properties are now effectively unused, and the session always queues
+asynchronous requests in the <link
+linkend="GMainContext"><type>GMainContext</type></link> that was is
+the thread default when the asynchronous operation is started. Session
+bookkeeping tasks (like closing idle connections) happen in the
+context that was thread default when the session was created.
+</para>
+
+<para>
+Additionally, <link
+linkend="soup-session-cancel-message"><function>soup_session_cancel_message()</function></link>
+now acts asynchronously when you cancel an asynchronous request;
+rather than having the request's callback be called from inside
+<function>soup_session_cancel_message()</function>, it just gets called
+when you need return to the main loop.
+</para>
+
+</refsect2>
+
+</refentry>
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 00000000..1609ff24
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,14 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -DSRCDIR=\""$(abs_srcdir)"\" \
+ -DBUILDDIR=\""$(builddir)"\" \
+ $(GLIB_CFLAGS)
+
+LIBS = \
+ $(top_builddir)/libsoup/libsoup-2.4.la \
+ $(GLIB_LIBS)
+
+noinst_PROGRAMS = \
+ get \
+ simple-httpd \
+ simple-proxy
diff --git a/examples/get.c b/examples/get.c
new file mode 100644
index 00000000..a8888d49
--- /dev/null
+++ b/examples/get.c
@@ -0,0 +1,195 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ * Copyright (C) 2013 Igalia, S.L.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libsoup/soup.h>
+
+static SoupSession *session;
+static GMainLoop *loop;
+static gboolean debug, head, quiet;
+static const gchar *output_file_path = NULL;
+
+static void
+finished (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+ g_main_loop_quit (loop);
+}
+
+static void
+get_url (const char *url)
+{
+ const char *name;
+ SoupMessage *msg;
+ const char *header;
+ FILE *output_file = NULL;
+
+ msg = soup_message_new (head ? "HEAD" : "GET", url);
+ soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
+
+ if (loop) {
+ g_object_ref (msg);
+ soup_session_queue_message (session, msg, finished, loop);
+ g_main_loop_run (loop);
+ } else
+ soup_session_send_message (session, msg);
+
+ name = soup_message_get_uri (msg)->path;
+
+ if (!debug) {
+ if (msg->status_code == SOUP_STATUS_SSL_FAILED) {
+ GTlsCertificateFlags flags;
+
+ if (soup_message_get_https_status (msg, NULL, &flags))
+ g_print ("%s: %d %s (0x%x)\n", name, msg->status_code, msg->reason_phrase, flags);
+ else
+ g_print ("%s: %d %s (no handshake status)\n", name, msg->status_code, msg->reason_phrase);
+ } else if (!quiet || SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
+ g_print ("%s: %d %s\n", name, msg->status_code, msg->reason_phrase);
+ }
+
+ if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
+ header = soup_message_headers_get_one (msg->response_headers,
+ "Location");
+ if (header) {
+ SoupURI *uri;
+ char *uri_string;
+
+ if (!debug && !quiet)
+ g_print (" -> %s\n", header);
+
+ uri = soup_uri_new_with_base (soup_message_get_uri (msg), header);
+ uri_string = soup_uri_to_string (uri, FALSE);
+ get_url (uri_string);
+ g_free (uri_string);
+ soup_uri_free (uri);
+ }
+ } else if (!head && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ if (output_file_path) {
+ output_file = fopen (output_file_path, "w");
+ if (!output_file)
+ g_printerr ("Error trying to create file %s.\n", output_file_path);
+ } else if (!quiet)
+ output_file = stdout;
+
+ if (output_file) {
+ fwrite (msg->response_body->data,
+ 1,
+ msg->response_body->length,
+ output_file);
+
+ if (output_file_path)
+ fclose (output_file);
+ }
+ }
+}
+
+static const char *ca_file, *proxy;
+static gboolean synchronous, ntlm;
+
+static GOptionEntry entries[] = {
+ { "ca-file", 'c', 0,
+ G_OPTION_ARG_STRING, &ca_file,
+ "Use FILE as the TLS CA file", "FILE" },
+ { "debug", 'd', 0,
+ G_OPTION_ARG_NONE, &debug,
+ "Show HTTP headers", NULL },
+ { "head", 'h', 0,
+ G_OPTION_ARG_NONE, &head,
+ "Do HEAD rather than GET", NULL },
+ { "ntlm", 'n', 0,
+ G_OPTION_ARG_NONE, &ntlm,
+ "Use NTLM authentication", NULL },
+ { "output", 'o', 0,
+ G_OPTION_ARG_STRING, &output_file_path,
+ "Write the received data to FILE instead of stdout", "FILE" },
+ { "proxy", 'p', 0,
+ G_OPTION_ARG_STRING, &proxy,
+ "Use URL as an HTTP proxy", "URL" },
+ { "quiet", 'q', 0,
+ G_OPTION_ARG_NONE, &quiet,
+ "Don't show HTTP status code", NULL },
+ { "sync", 's', 0,
+ G_OPTION_ARG_NONE, &synchronous,
+ "Use SoupSessionSync rather than SoupSessionAsync", NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ GOptionContext *opts;
+ const char *url;
+ SoupURI *proxy_uri, *parsed;
+ GError *error = NULL;
+ SoupLogger *logger = NULL;
+
+ opts = g_option_context_new (NULL);
+ g_option_context_add_main_entries (opts, entries, NULL);
+ if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+ g_printerr ("Could not parse arguments: %s\n",
+ error->message);
+ g_printerr ("%s",
+ g_option_context_get_help (opts, TRUE, NULL));
+ exit (1);
+ }
+
+ if (argc != 2) {
+ g_printerr ("%s",
+ g_option_context_get_help (opts, TRUE, NULL));
+ exit (1);
+ }
+ g_option_context_free (opts);
+
+ url = argv[1];
+ parsed = soup_uri_new (url);
+ if (!parsed) {
+ g_printerr ("Could not parse '%s' as a URL\n", url);
+ exit (1);
+ }
+ soup_uri_free (parsed);
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_SSL_CA_FILE, ca_file,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+ SOUP_SESSION_USER_AGENT, "get ",
+ SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
+ NULL);
+ if (ntlm)
+ soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+
+ if (debug) {
+ logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
+ g_object_unref (logger);
+ }
+
+ if (proxy) {
+ proxy_uri = soup_uri_new (proxy);
+ if (!proxy_uri) {
+ g_printerr ("Could not parse '%s' as URI\n",
+ proxy);
+ exit (1);
+ }
+
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ NULL);
+ soup_uri_free (proxy_uri);
+ }
+
+ if (!synchronous)
+ loop = g_main_loop_new (NULL, TRUE);
+
+ get_url (url);
+
+ if (!synchronous)
+ g_main_loop_unref (loop);
+
+ return 0;
+}
diff --git a/tests/simple-httpd.c b/examples/simple-httpd.c
index 46f0988d..71ff874c 100644
--- a/tests/simple-httpd.c
+++ b/examples/simple-httpd.c
@@ -3,32 +3,14 @@
* Copyright (C) 2001-2003, Ximian, Inc.
*/
-#include "test-utils.h"
-
#include <dirent.h>
#include <errno.h>
-#include <fcntl.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
-#ifdef HAVE_MMAP
-#include <sys/mman.h>
-#endif
-
-#ifdef HAVE_MMAP
-struct mapping {
- void *start;
- size_t length;
-};
-
-static void
-free_mapping (gpointer data)
-{
- struct mapping *mapping = data;
- munmap (mapping->start, mapping->length);
- g_slice_free (struct mapping, mapping);
-}
-#endif
+#include <libsoup/soup.h>
static int
compare_strings (gconstpointer a, gconstpointer b)
@@ -87,7 +69,6 @@ do_get (SoupServer *server, SoupMessage *msg, const char *path)
{
char *slash;
struct stat st;
- int fd;
if (stat (path, &st) == -1) {
if (errno == EPERM)
@@ -130,40 +111,27 @@ do_get (SoupServer *server, SoupMessage *msg, const char *path)
return;
}
- fd = open (path, O_RDONLY);
- if (fd == -1) {
- soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
- return;
- }
-
if (msg->method == SOUP_METHOD_GET) {
-#ifdef HAVE_MMAP
- struct mapping *mapping = g_slice_new (struct mapping);
+ GMappedFile *mapping;
SoupBuffer *buffer;
- mapping->start = mmap (NULL, st.st_size, PROT_READ,
- MAP_PRIVATE, fd, 0);
- mapping->length = st.st_size;
- buffer = soup_buffer_new_with_owner (mapping->start,
- mapping->length,
- mapping, free_mapping);
+ mapping = g_mapped_file_new (path, FALSE, NULL);
+ if (!mapping) {
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ buffer = soup_buffer_new_with_owner (g_mapped_file_get_contents (mapping),
+ g_mapped_file_get_length (mapping),
+ mapping, (GDestroyNotify)g_mapped_file_unref);
soup_message_body_append_buffer (msg->response_body, buffer);
soup_buffer_free (buffer);
-#else
- char *buf;
-
- buf = g_malloc (st.st_size);
- read (fd, buf, st.st_size);
- close (fd);
- soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
- buf, st.st_size);
-#endif
} else /* msg->method == SOUP_METHOD_HEAD */ {
char *length;
/* We could just use the same code for both GET and
- * HEAD. But we'll optimize and avoid the extra
- * malloc.
+ * HEAD (soup-message-server-io.c will fix things up).
+ * But we'll optimize and avoid the extra I/O.
*/
length = g_strdup_printf ("%lu", (gulong)st.st_size);
soup_message_headers_append (msg->response_headers,
@@ -245,40 +213,51 @@ quit (int sig)
exit (0);
}
+static int port, ssl_port;
+static const char *ssl_cert_file, *ssl_key_file;
+
+static GOptionEntry entries[] = {
+ { "cert-file", 'c', 0,
+ G_OPTION_ARG_STRING, &ssl_cert_file,
+ "Use FILE as the TLS certificate file", "FILE" },
+ { "key-file", 'k', 0,
+ G_OPTION_ARG_STRING, &ssl_key_file,
+ "Use FILE as the TLS private key file", "FILE" },
+ { "port", 'p', 0,
+ G_OPTION_ARG_INT, &port,
+ "Port to listen on", NULL },
+ { "ssl-port", 's', 0,
+ G_OPTION_ARG_INT, &port,
+ "Port to listen on for TLS traffic", NULL },
+ { NULL }
+};
+
int
main (int argc, char **argv)
{
+ GOptionContext *opts;
GMainLoop *loop;
SoupServer *server, *ssl_server;
- int opt;
- int port = SOUP_ADDRESS_ANY_PORT;
- int ssl_port = SOUP_ADDRESS_ANY_PORT;
- const char *ssl_cert_file = NULL, *ssl_key_file = NULL;
+ GError *error = NULL;
+
+ opts = g_option_context_new (NULL);
+ g_option_context_add_main_entries (opts, entries, NULL);
+ if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+ g_printerr ("Could not parse arguments: %s\n",
+ error->message);
+ g_printerr ("%s",
+ g_option_context_get_help (opts, TRUE, NULL));
+ exit (1);
+ }
+ if (argc != 1) {
+ g_printerr ("%s",
+ g_option_context_get_help (opts, TRUE, NULL));
+ exit (1);
+ }
+ g_option_context_free (opts);
- g_type_init ();
signal (SIGINT, quit);
- while ((opt = getopt (argc, argv, "p:k:c:s:")) != -1) {
- switch (opt) {
- case 'p':
- port = atoi (optarg);
- break;
- case 'k':
- ssl_key_file = optarg;
- break;
- case 'c':
- ssl_cert_file = optarg;
- break;
- case 's':
- ssl_port = atoi (optarg);
- break;
- default:
- g_printerr ("Usage: %s [-p port] [-c ssl-cert-file -k ssl-key-file [-s ssl-port]]\n",
- argv[0]);
- exit (1);
- }
- }
-
server = soup_server_new (SOUP_SERVER_PORT, port,
SOUP_SERVER_SERVER_HEADER, "simple-httpd ",
NULL);
diff --git a/tests/simple-proxy.c b/examples/simple-proxy.c
index f25f9a04..db8c9f96 100644
--- a/tests/simple-proxy.c
+++ b/examples/simple-proxy.c
@@ -3,7 +3,10 @@
* Copyright (C) 2001-2003, Ximian, Inc.
*/
-#include "test-utils.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include <libsoup/soup.h>
/* WARNING: this is really really really not especially compliant with
* RFC 2616. But it does work for basic stuff.
@@ -120,35 +123,44 @@ quit (int sig)
exit (0);
}
+static int port;
+static gboolean require_auth;
+
+static GOptionEntry entries[] = {
+ { "auth-domain", 'a', 0,
+ G_OPTION_ARG_NONE, &require_auth,
+ "Require authentication", NULL },
+ { "port", 'p', 0,
+ G_OPTION_ARG_INT, &port,
+ "Port to listen on", NULL },
+ { NULL }
+};
+
int
main (int argc, char **argv)
{
+ GOptionContext *opts;
GMainLoop *loop;
- int opt;
- int port = SOUP_ADDRESS_ANY_PORT;
- SoupAuthDomain *auth_domain = NULL;
-
- g_type_init ();
- signal (SIGINT, quit);
+ GError *error = NULL;
+
+ opts = g_option_context_new (NULL);
+ g_option_context_add_main_entries (opts, entries, NULL);
+ if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+ g_printerr ("Could not parse arguments: %s\n",
+ error->message);
+ g_printerr ("%s",
+ g_option_context_get_help (opts, TRUE, NULL));
+ exit (1);
+ }
- while ((opt = getopt (argc, argv, "ap:")) != -1) {
- switch (opt) {
- case 'a':
- auth_domain = soup_auth_domain_basic_new (
- SOUP_AUTH_DOMAIN_REALM, "simple-proxy",
- SOUP_AUTH_DOMAIN_PROXY, TRUE,
- SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
- NULL);
- break;
- case 'p':
- port = atoi (optarg);
- break;
- default:
- g_printerr ("Usage: %s [-p port] [-n]\n",
- argv[0]);
- exit (1);
- }
+ if (argc != 1) {
+ g_printerr ("%s",
+ g_option_context_get_help (opts, TRUE, NULL));
+ exit (1);
}
+ g_option_context_free (opts);
+
+ signal (SIGINT, quit);
server = soup_server_new (SOUP_SERVER_PORT, port,
NULL);
@@ -158,7 +170,14 @@ main (int argc, char **argv)
}
soup_server_add_handler (server, NULL,
server_callback, NULL, NULL);
- if (auth_domain) {
+ if (require_auth) {
+ SoupAuthDomain *auth_domain;
+
+ auth_domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "simple-proxy",
+ SOUP_AUTH_DOMAIN_PROXY, TRUE,
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
+ NULL);
soup_server_add_auth_domain (server, auth_domain);
g_object_unref (auth_domain);
}
diff --git a/glib-tap.mk b/glib-tap.mk
new file mode 100644
index 00000000..c97bf3f2
--- /dev/null
+++ b/glib-tap.mk
@@ -0,0 +1,134 @@
+# GLIB - Library of useful C routines
+
+TESTS_ENVIRONMENT= \
+ G_TEST_SRCDIR="$(abs_srcdir)" \
+ G_TEST_BUILDDIR="$(abs_builddir)" \
+ G_DEBUG=gc-friendly \
+ MALLOC_CHECK_=2 \
+ MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
+LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh
+LOG_COMPILER = $(top_srcdir)/tap-test
+
+NULL =
+
+# initialize variables for unconditional += appending
+BUILT_SOURCES =
+BUILT_EXTRA_DIST =
+CLEANFILES = *.log *.trs
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+EXTRA_DIST =
+TESTS =
+
+installed_test_LTLIBRARIES =
+installed_test_PROGRAMS =
+installed_test_SCRIPTS =
+nobase_installed_test_DATA =
+
+noinst_LTLIBRARIES =
+noinst_PROGRAMS =
+noinst_SCRIPTS =
+noinst_DATA =
+
+check_LTLIBRARIES =
+check_PROGRAMS =
+check_SCRIPTS =
+check_DATA =
+
+# We support a fairly large range of possible variables. It is expected that all types of files in a test suite
+# will belong in exactly one of the following variables.
+#
+# First, we support the usual automake suffixes, but in lowercase, with the customary meaning:
+#
+# test_programs, test_scripts, test_data, test_ltlibraries
+#
+# The above are used to list files that are involved in both uninstalled and installed testing. The
+# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite.
+# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data is
+# installed in the same way as it appears in the package layout.
+#
+# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled',
+# like so:
+#
+# installed_test_programs, uninstalled_test_programs
+# installed_test_scripts, uninstalled_test_scripts
+# installed_test_data, uninstalled_test_data
+# installed_test_ltlibraries, uninstalled_test_ltlibraries
+#
+# Additionally, we support 'extra' infixes for programs and scripts. This is used for support programs/scripts
+# that should not themselves be run as testcases (but exist to be used from other testcases):
+#
+# test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs
+# test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts
+#
+# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or data
+# file automatically end up in the tarball.
+#
+# dist_test_scripts, dist_test_data, dist_test_extra_scripts
+# dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts
+# dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts
+#
+# Note that no file is automatically disted unless it appears in one of the dist_ variables. This follows the
+# standard automake convention of not disting programs scripts or data by default.
+#
+# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their disted
+# variants) will be run as part of the in-tree 'make check'. These are all assumed to be runnable under
+# gtester. That's a bit strange for scripts, but it's possible.
+
+TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \
+ $(dist_test_scripts) $(dist_uninstalled_test_scripts)
+
+# Note: build even the installed-only targets during 'make check' to ensure that they still work.
+# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to
+# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were
+# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'.
+all_test_programs = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \
+ $(test_extra_programs) $(uninstalled_test_extra_programs) $(installed_test_extra_programs)
+all_test_scripts = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \
+ $(test_extra_scripts) $(uninstalled_test_extra_scripts) $(installed_test_extra_scripts)
+all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) \
+ $(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) $(dist_installed_test_extra_scripts)
+all_test_scripts += $(all_dist_test_scripts)
+EXTRA_DIST += $(all_dist_test_scripts)
+all_test_data = $(test_data) $(uninstalled_test_data) $(installed_test_data)
+all_dist_test_data = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data)
+all_test_data += $(all_dist_test_data)
+EXTRA_DIST += $(all_dist_test_data)
+all_test_ltlibs = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries)
+
+if ENABLE_ALWAYS_BUILD_TESTS
+noinst_LTLIBRARIES += $(all_test_ltlibs)
+noinst_PROGRAMS += $(all_test_programs)
+noinst_SCRIPTS += $(all_test_scripts)
+noinst_DATA += $(all_test_data)
+else
+check_LTLIBRARIES += $(all_test_ltlibs)
+check_PROGRAMS += $(all_test_programs)
+check_SCRIPTS += $(all_test_scripts)
+check_DATA += $(all_test_data)
+endif
+
+if ENABLE_INSTALLED_TESTS
+installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \
+ $(test_extra_programs) $(installed_test_extra_programs)
+installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \
+ $(test_extra_scripts) $(test_installed_extra_scripts)
+installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \
+ $(dist_installed_test_scripts) $(dist_installed_test_extra_scripts)
+nobase_installed_test_DATA += $(test_data) $(installed_test_data)
+nobase_installed_test_DATA += $(dist_test_data) $(dist_installed_test_data)
+installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries)
+installed_testcases = $(test_programs) $(installed_test_programs) \
+ $(test_scripts) $(installed_test_scripts) \
+ $(dist_test_scripts) $(dist_installed_test_scripts)
+
+installed_test_meta_DATA = $(installed_testcases:=.test)
+
+%.test: %$(EXEEXT) Makefile
+ $(AM_V_GEN) (echo '[Test]' > $@.tmp; \
+ echo 'Type=session' >> $@.tmp; \
+ echo 'Exec=$(installed_testdir)/$<' >> $@.tmp; \
+ mv $@.tmp $@)
+
+CLEANFILES += $(installed_test_meta_DATA)
+endif
diff --git a/libsoup.doap b/libsoup.doap
index 00028649..c39f2d5d 100644
--- a/libsoup.doap
+++ b/libsoup.doap
@@ -12,6 +12,7 @@
<download-page rdf:resource="http://download.gnome.org/sources/libsoup/"/>
<bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=libsoup"/>
<mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/libsoup-list"/>
+ <category rdf:resource="http://api.gnome.org/doap-extensions#platform"/>
<maintainer>
<foaf:Person>
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index fdba1efa..60920bdc 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -8,16 +8,15 @@ if OS_WIN32
LIBWS2_32 = -lws2_32
endif
-INCLUDES = \
+AM_CPPFLAGS = \
-DG_LOG_DOMAIN=\"libsoup\" \
-DLOCALEDIR=\"$(localedir)\" \
-I$(top_srcdir) \
+ -I$(top_builddir) \
$(SOUP_DEBUG_FLAGS) \
- $(SOUP_MAINTAINER_FLAGS) \
$(GLIB_CFLAGS) \
$(XML_CFLAGS) \
- $(SQLITE_CFLAGS) \
- $(GNOME_KEYRING_CFLAGS)
+ $(SQLITE_CFLAGS)
libsoupincludedir = $(includedir)/libsoup-2.4/libsoup
@@ -28,11 +27,13 @@ soup_headers = \
soup-auth-domain.h \
soup-auth-domain-basic.h \
soup-auth-domain-digest.h \
+ soup-auth-manager.h \
soup-cache.h \
soup-content-decoder.h \
soup-content-sniffer.h \
soup-cookie.h \
soup-cookie-jar.h \
+ soup-cookie-jar-db.h \
soup-cookie-jar-text.h \
soup-date.h \
soup-form.h \
@@ -44,6 +45,7 @@ soup_headers = \
soup-method.h \
soup-misc.h \
soup-multipart.h \
+ soup-multipart-input-stream.h \
soup-password-manager.h \
soup-portability.h \
soup-proxy-resolver.h \
@@ -71,16 +73,23 @@ libsoupinclude_HEADERS = \
$(soup_headers) \
soup-enum-types.h
+nodist_libsoupinclude_HEADERS = \
+ soup-version.h
+
lib_LTLIBRARIES = libsoup-2.4.la
libsoup_2_4_la_LDFLAGS = \
- -version-info $(SOUP_CURRENT):$(SOUP_REVISION):$(SOUP_AGE) -no-undefined
+ -version-info $(SOUP_CURRENT):$(SOUP_REVISION):$(SOUP_AGE) \
+ -no-undefined \
+ -export-symbols $(srcdir)/libsoup-2.4.sym
+
+EXTRA_DIST += libsoup-2.4.sym
libsoup_2_4_la_LIBADD = \
$(GLIB_LIBS) \
+ $(LIBWS2_32) \
$(XML_LIBS) \
- -lz \
- $(LIBWS2_32)
+ $(SQLITE_LIBS)
libsoup_2_4_la_SOURCES = \
soup-address.c \
@@ -94,21 +103,24 @@ libsoup_2_4_la_SOURCES = \
soup-auth-domain.c \
soup-auth-domain-basic.c \
soup-auth-domain-digest.c \
- soup-auth-manager.h \
soup-auth-manager.c \
- soup-auth-manager-ntlm.h \
- soup-auth-manager-ntlm.c \
soup-body-input-stream.h \
soup-body-input-stream.c \
soup-body-output-stream.h \
soup-body-output-stream.c \
soup-cache.c \
+ soup-cache-input-stream.h \
+ soup-cache-input-stream.c \
soup-cache-private.h \
soup-client-input-stream.h \
soup-client-input-stream.c \
soup-connection.h \
soup-connection.c \
+ soup-connection-auth.h \
+ soup-connection-auth.c \
soup-content-decoder.c \
+ soup-content-processor.h \
+ soup-content-processor.c \
soup-content-sniffer.c \
soup-content-sniffer-stream.h \
soup-content-sniffer-stream.c \
@@ -116,6 +128,7 @@ libsoup_2_4_la_SOURCES = \
soup-converter-wrapper.c \
soup-cookie.c \
soup-cookie-jar.c \
+ soup-cookie-jar-db.c \
soup-cookie-jar-text.c \
soup-date.c \
soup-directory-input-stream.h \
@@ -129,8 +142,6 @@ libsoup_2_4_la_SOURCES = \
soup-io-stream.h \
soup-io-stream.c \
soup-logger.c \
- soup-marshal.h \
- soup-marshal.c \
soup-message.c \
soup-message-body.c \
soup-message-client-io.c \
@@ -144,13 +155,14 @@ libsoup_2_4_la_SOURCES = \
soup-misc.c \
soup-misc-private.h \
soup-multipart.c \
+ soup-multipart-input-stream.c \
soup-password-manager.c \
soup-path-map.h \
soup-path-map.c \
soup-proxy-resolver.c \
soup-proxy-resolver-default.c \
- soup-proxy-resolver-static.h \
- soup-proxy-resolver-static.c \
+ soup-proxy-resolver-wrapper.h \
+ soup-proxy-resolver-wrapper.c \
soup-proxy-uri-resolver.c \
soup-request.c \
soup-request-data.c \
@@ -169,6 +181,7 @@ libsoup_2_4_la_SOURCES = \
soup-tld-private.h \
soup-uri.c \
soup-value-utils.c \
+ soup-version.c \
soup-xmlrpc.c
# TLD rules
@@ -181,14 +194,6 @@ tld_data.inc: tld-parser.py $(TLD_DATA_FILE)
if BUILD_LIBSOUP_GNOME
-if OS_WIN32
-soup_password_manager_gnome_files =
-else
-soup_password_manager_gnome_files = \
- soup-password-manager-gnome.h \
- soup-password-manager-gnome.c
-endif
-
libsoupgnomeincludedir = $(includedir)/libsoup-gnome-2.4/libsoup
libsoupgnomeinclude_HEADERS = \
@@ -198,30 +203,32 @@ libsoupgnomeinclude_HEADERS = \
lib_LTLIBRARIES += libsoup-gnome-2.4.la
-libsoup_gnome_2_4_la_LDFLAGS = $(libsoup_2_4_la_LDFLAGS)
+libsoup_gnome_2_4_la_LDFLAGS = \
+ -version-info $(SOUP_CURRENT):$(SOUP_REVISION):$(SOUP_AGE) \
+ -no-undefined \
+ -export-symbols $(srcdir)/libsoup-gnome-2.4.sym
+
+EXTRA_DIST += libsoup-gnome-2.4.sym
libsoup_gnome_2_4_la_LIBADD = \
libsoup-2.4.la \
- $(GLIB_LIBS) \
- $(SQLITE_LIBS) \
- $(GNOME_KEYRING_LIBS)
+ $(GLIB_LIBS)
libsoup_gnome_2_4_la_SOURCES = \
soup-cookie-jar-sqlite.c \
soup-gnome-features.c \
soup-proxy-resolver-gnome.h \
soup-proxy-resolver-gnome.c \
- $(soup_password_manager_gnome_files)
+ soup-password-manager-gnome.h \
+ soup-password-manager-gnome.c
endif
-GLIB_GENERATED = soup-marshal.c soup-marshal.h
-GLIB_GENERATED += soup-enum-types.c soup-enum-types.h
+GLIB_GENERATED = soup-enum-types.c soup-enum-types.h
BUILT_SOURCES = \
$(GLIB_GENERATED) \
tld_data.inc
-soup_marshal_sources = $(libsoup_2_4_la_SOURCES) $(libsoup_gnome_2_4_la_SOURCES)
soup_enum_types_sources = $(libsoupinclude_HEADERS) $(libsoupgnomeinclude_HEADERS)
soup_enum_types_MKENUMS_C_FLAGS = --fhead "\#define LIBSOUP_USE_UNSTABLE_REQUEST_API"
@@ -237,13 +244,13 @@ if HAVE_INTROSPECTION
# Core library
gi_soup_files = \
- $(filter-out soup.h soup-enum-types.% soup-marshal.% soup-proxy-resolver.h,\
+ $(filter-out soup.h soup-enum-types.% soup-proxy-resolver.h,\
$(soup_headers) $(filter-out %.h, $(libsoup_2_4_la_SOURCES)))
gi_built_soup_files = soup-enum-types.h
Soup-2.4.gir: libsoup-2.4.la
Soup_2_4_gir_INCLUDES = Gio-2.0
-Soup_2_4_gir_CFLAGS = $(INCLUDES) -DLIBSOUP_USE_UNSTABLE_REQUEST_API
+Soup_2_4_gir_CFLAGS = $(AM_CPPFLAGS) -DLIBSOUP_USE_UNSTABLE_REQUEST_API
Soup_2_4_gir_LIBS = libsoup-2.4.la
Soup_2_4_gir_EXPORT_PACKAGES = libsoup-2.4
Soup_2_4_gir_SCANNERFLAGS = --c-include "libsoup/soup.h"
@@ -268,7 +275,7 @@ SoupGNOME_2_4_gir_SCANNERFLAGS = \
--symbol-prefix=soup \
--c-include "libsoup/soup-gnome.h" \
--include-uninstalled=$(builddir)/Soup-2.4.gir
-SoupGNOME_2_4_gir_CFLAGS = $(INCLUDES)
+SoupGNOME_2_4_gir_CFLAGS = $(AM_CPPFLAGS)
SoupGNOME_2_4_gir_LIBS = libsoup-gnome-2.4.la libsoup-2.4.la
SoupGNOME_2_4_gir_FILES = $(addprefix $(srcdir)/,$(gi_soup_gnome_files))
SoupGNOME_2_4_gir_EXPORT_PACKAGES = libsoup-gnome-2.4
diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
new file mode 100644
index 00000000..be4cd7f4
--- /dev/null
+++ b/libsoup/libsoup-2.4.sym
@@ -0,0 +1,508 @@
+soup_add_completion
+soup_add_idle
+soup_add_io_watch
+soup_address_equal_by_ip
+soup_address_equal_by_name
+soup_address_family_get_type
+soup_address_get_gsockaddr
+soup_address_get_name
+soup_address_get_physical
+soup_address_get_port
+soup_address_get_sockaddr
+soup_address_get_type
+soup_address_hash_by_ip
+soup_address_hash_by_name
+soup_address_is_resolved
+soup_address_new
+soup_address_new_any
+soup_address_new_from_sockaddr
+soup_address_resolve_async
+soup_address_resolve_sync
+soup_add_timeout
+soup_auth_authenticate
+soup_auth_basic_get_type
+soup_auth_digest_get_type
+soup_auth_domain_accepts
+soup_auth_domain_add_path
+soup_auth_domain_basic_get_type
+soup_auth_domain_basic_new
+soup_auth_domain_basic_set_auth_callback
+soup_auth_domain_challenge
+soup_auth_domain_check_password
+soup_auth_domain_covers
+soup_auth_domain_digest_encode_password
+soup_auth_domain_digest_get_type
+soup_auth_domain_digest_new
+soup_auth_domain_digest_set_auth_callback
+soup_auth_domain_get_realm
+soup_auth_domain_get_type
+soup_auth_domain_remove_path
+soup_auth_domain_set_filter
+soup_auth_domain_set_generic_auth_callback
+soup_auth_domain_try_generic_auth_callback
+soup_auth_free_protection_space
+soup_auth_get_authorization
+soup_auth_get_host
+soup_auth_get_info
+soup_auth_get_protection_space
+soup_auth_get_realm
+soup_auth_get_saved_password
+soup_auth_get_saved_users
+soup_auth_get_scheme_name
+soup_auth_get_type
+soup_auth_has_saved_password
+soup_auth_is_authenticated
+soup_auth_is_for_proxy
+soup_auth_is_ready
+soup_auth_manager_get_type
+soup_auth_manager_use_auth
+soup_auth_new
+soup_auth_ntlm_get_type
+soup_auth_save_password
+soup_auth_update
+soup_buffer_copy
+soup_buffer_free
+soup_buffer_get_as_bytes
+soup_buffer_get_data
+soup_buffer_get_owner
+soup_buffer_get_type
+soup_buffer_new
+soup_buffer_new_subbuffer
+soup_buffer_new_take
+soup_buffer_new_with_owner
+soup_byte_array_get_type
+soup_cacheability_get_type
+soup_cache_clear
+soup_cache_dump
+soup_cache_flush
+soup_cache_get_max_size
+soup_cache_get_type
+soup_cache_load
+soup_cache_new
+soup_cache_response_get_type
+soup_cache_set_max_size
+soup_cache_type_get_type
+soup_char_attributes
+soup_check_version
+soup_client_context_get_address
+soup_client_context_get_auth_domain
+soup_client_context_get_auth_user
+soup_client_context_get_host
+soup_client_context_get_socket
+soup_client_context_get_type
+soup_connection_state_get_type
+soup_content_decoder_get_type
+soup_content_sniffer_get_buffer_size
+soup_content_sniffer_get_type
+soup_content_sniffer_new
+soup_content_sniffer_sniff
+soup_cookie_applies_to_uri
+soup_cookie_copy
+soup_cookie_domain_matches
+soup_cookie_equal
+soup_cookie_free
+soup_cookie_get_domain
+soup_cookie_get_expires
+soup_cookie_get_http_only
+soup_cookie_get_name
+soup_cookie_get_path
+soup_cookie_get_secure
+soup_cookie_get_type
+soup_cookie_get_value
+soup_cookie_jar_accept_policy_get_type
+soup_cookie_jar_add_cookie
+soup_cookie_jar_add_cookie_with_first_party
+soup_cookie_jar_all_cookies
+soup_cookie_jar_db_get_type
+soup_cookie_jar_db_new
+soup_cookie_jar_delete_cookie
+soup_cookie_jar_get_accept_policy
+soup_cookie_jar_get_cookie_list
+soup_cookie_jar_get_cookies
+soup_cookie_jar_get_type
+soup_cookie_jar_is_persistent
+soup_cookie_jar_new
+soup_cookie_jar_save
+soup_cookie_jar_set_accept_policy
+soup_cookie_jar_set_cookie
+soup_cookie_jar_set_cookie_with_first_party
+soup_cookie_jar_text_get_type
+soup_cookie_jar_text_new
+soup_cookie_new
+soup_cookie_parse
+soup_cookie_set_domain
+soup_cookie_set_expires
+soup_cookie_set_http_only
+soup_cookie_set_max_age
+soup_cookie_set_name
+soup_cookie_set_path
+soup_cookie_set_secure
+soup_cookie_set_value
+soup_cookies_free
+soup_cookies_from_request
+soup_cookies_from_response
+soup_cookies_to_cookie_header
+soup_cookies_to_request
+soup_cookies_to_response
+soup_cookie_to_cookie_header
+soup_cookie_to_set_cookie_header
+soup_date_copy
+soup_date_format_get_type
+soup_date_free
+soup_date_get_day
+soup_date_get_hour
+soup_date_get_minute
+soup_date_get_month
+soup_date_get_offset
+soup_date_get_second
+soup_date_get_type
+soup_date_get_utc
+soup_date_get_year
+soup_date_is_past
+soup_date_new
+soup_date_new_from_now
+soup_date_new_from_string
+soup_date_new_from_time_t
+soup_date_to_string
+soup_date_to_time_t
+soup_date_to_timeval
+soup_encoding_get_type
+soup_expectation_get_type
+soup_form_decode
+soup_form_decode_multipart
+soup_form_encode
+soup_form_encode_datalist
+soup_form_encode_hash
+soup_form_encode_valist
+soup_form_request_new
+soup_form_request_new_from_datalist
+soup_form_request_new_from_hash
+soup_form_request_new_from_multipart
+soup_get_major_version
+soup_get_micro_version
+soup_get_minor_version
+soup_header_contains
+soup_header_free_list
+soup_header_free_param_list
+soup_header_g_string_append_param
+soup_header_g_string_append_param_quoted
+soup_header_parse_list
+soup_header_parse_param_list
+soup_header_parse_quality_list
+soup_header_parse_semi_param_list
+soup_headers_parse
+soup_headers_parse_request
+soup_headers_parse_response
+soup_headers_parse_status_line
+soup_http_error_quark
+soup_http_version_get_type
+soup_known_status_code_get_type
+soup_logger_attach
+soup_logger_detach
+soup_logger_get_type
+soup_logger_log_level_get_type
+soup_logger_new
+soup_logger_set_printer
+soup_logger_set_request_filter
+soup_logger_set_response_filter
+soup_memory_use_get_type
+soup_message_add_header_handler
+soup_message_add_status_code_handler
+soup_message_body_append
+soup_message_body_append_buffer
+soup_message_body_append_take
+soup_message_body_complete
+soup_message_body_flatten
+soup_message_body_free
+soup_message_body_get_accumulate
+soup_message_body_get_chunk
+soup_message_body_get_type
+soup_message_body_got_chunk
+soup_message_body_new
+soup_message_body_set_accumulate
+soup_message_body_truncate
+soup_message_body_wrote_chunk
+soup_message_content_sniffed
+soup_message_disable_feature
+soup_message_finished
+soup_message_flags_get_type
+soup_message_get_address
+soup_message_get_first_party
+soup_message_get_flags
+soup_message_get_https_status
+soup_message_get_http_version
+soup_message_get_priority
+soup_message_get_soup_request
+soup_message_get_type
+soup_message_get_uri
+soup_message_got_body
+soup_message_got_chunk
+soup_message_got_headers
+soup_message_got_informational
+soup_message_headers_append
+soup_message_headers_clean_connection_headers
+soup_message_headers_clear
+soup_message_headers_foreach
+soup_message_headers_free
+soup_message_headers_free_ranges
+soup_message_headers_get
+soup_message_headers_get_content_disposition
+soup_message_headers_get_content_length
+soup_message_headers_get_content_range
+soup_message_headers_get_content_type
+soup_message_headers_get_encoding
+soup_message_headers_get_expectations
+soup_message_headers_get_list
+soup_message_headers_get_one
+soup_message_headers_get_ranges
+soup_message_headers_get_type
+soup_message_headers_iter_init
+soup_message_headers_iter_next
+soup_message_headers_new
+soup_message_headers_remove
+soup_message_headers_replace
+soup_message_headers_set_content_disposition
+soup_message_headers_set_content_length
+soup_message_headers_set_content_range
+soup_message_headers_set_content_type
+soup_message_headers_set_encoding
+soup_message_headers_set_expectations
+soup_message_headers_set_range
+soup_message_headers_set_ranges
+soup_message_headers_type_get_type
+soup_message_io_cleanup
+soup_message_is_keepalive
+soup_message_new
+soup_message_new_from_uri
+soup_message_priority_get_type
+soup_message_restarted
+soup_message_set_chunk_allocator
+soup_message_set_first_party
+soup_message_set_flags
+soup_message_set_http_version
+soup_message_set_priority
+soup_message_set_redirect
+soup_message_set_request
+soup_message_set_response
+soup_message_set_status
+soup_message_set_status_full
+soup_message_set_uri
+soup_message_wrote_body
+soup_message_wrote_body_data
+soup_message_wrote_chunk
+soup_message_wrote_headers
+soup_message_wrote_informational
+soup_multipart_append_form_file
+soup_multipart_append_form_string
+soup_multipart_append_part
+soup_multipart_free
+soup_multipart_get_length
+soup_multipart_get_part
+soup_multipart_get_type
+soup_multipart_input_stream_get_headers
+soup_multipart_input_stream_get_type
+soup_multipart_input_stream_new
+soup_multipart_input_stream_next_part
+soup_multipart_input_stream_next_part_async
+soup_multipart_input_stream_next_part_finish
+soup_multipart_new
+soup_multipart_new_from_message
+soup_multipart_to_message
+soup_password_manager_get_passwords_async
+soup_password_manager_get_passwords_sync
+soup_password_manager_get_type
+soup_proxy_resolver_default_get_type
+soup_proxy_resolver_get_proxy_async
+soup_proxy_resolver_get_proxy_sync
+soup_proxy_resolver_get_type
+soup_proxy_uri_resolver_get_proxy_uri_async
+soup_proxy_uri_resolver_get_proxy_uri_sync
+soup_proxy_uri_resolver_get_type
+soup_request_data_get_type
+soup_requester_error_get_type
+soup_requester_error_quark
+soup_requester_get_type
+soup_requester_new
+soup_requester_request
+soup_requester_request_uri
+soup_request_error_get_type
+soup_request_error_quark
+soup_request_file_get_file
+soup_request_file_get_type
+soup_request_get_content_length
+soup_request_get_content_type
+soup_request_get_session
+soup_request_get_type
+soup_request_get_uri
+soup_request_http_get_message
+soup_request_http_get_type
+soup_request_send
+soup_request_send_async
+soup_request_send_finish
+soup_server_add_auth_domain
+soup_server_add_handler
+soup_server_disconnect
+soup_server_get_async_context
+soup_server_get_listener
+soup_server_get_port
+soup_server_get_type
+soup_server_is_https
+soup_server_new
+soup_server_pause_message
+soup_server_quit
+soup_server_remove_auth_domain
+soup_server_remove_handler
+soup_server_run
+soup_server_run_async
+soup_server_unpause_message
+soup_session_abort
+soup_session_add_feature
+soup_session_add_feature_by_type
+soup_session_async_get_type
+soup_session_async_new
+soup_session_async_new_with_options
+soup_session_cancel_message
+soup_session_feature_add_feature
+soup_session_feature_attach
+soup_session_feature_detach
+soup_session_feature_get_type
+soup_session_feature_has_feature
+soup_session_feature_remove_feature
+soup_session_get_async_context
+soup_session_get_feature
+soup_session_get_feature_for_message
+soup_session_get_features
+soup_session_get_type
+soup_session_has_feature
+soup_session_new
+soup_session_new_with_options
+soup_session_pause_message
+soup_session_prefetch_dns
+soup_session_prepare_for_uri
+soup_session_queue_message
+soup_session_redirect_message
+soup_session_remove_feature
+soup_session_remove_feature_by_type
+soup_session_request
+soup_session_request_http
+soup_session_request_http_uri
+soup_session_request_uri
+soup_session_requeue_message
+soup_session_send
+soup_session_send_async
+soup_session_send_finish
+soup_session_send_message
+soup_session_sync_get_type
+soup_session_sync_new
+soup_session_sync_new_with_options
+soup_session_unpause_message
+soup_session_would_redirect
+soup_socket_connect_async
+soup_socket_connect_sync
+soup_socket_disconnect
+soup_socket_get_fd
+soup_socket_get_local_address
+soup_socket_get_remote_address
+soup_socket_get_type
+soup_socket_io_status_get_type
+soup_socket_is_connected
+soup_socket_is_ssl
+soup_socket_listen
+soup_socket_new
+soup_socket_read
+soup_socket_read_until
+soup_socket_start_proxy_ssl
+soup_socket_start_ssl
+soup_socket_write
+soup_ssl_supported
+soup_status_get_phrase
+soup_status_get_type
+soup_status_proxify
+soup_str_case_equal
+soup_str_case_hash
+soup_tld_domain_is_public_suffix
+soup_tld_error_get_type
+soup_tld_error_quark
+soup_tld_get_base_domain
+soup_uri_copy
+soup_uri_copy_host
+soup_uri_decode
+soup_uri_encode
+soup_uri_equal
+soup_uri_free
+soup_uri_get_fragment
+soup_uri_get_host
+soup_uri_get_password
+soup_uri_get_path
+soup_uri_get_port
+soup_uri_get_query
+soup_uri_get_scheme
+soup_uri_get_type
+soup_uri_get_user
+soup_uri_host_equal
+soup_uri_host_hash
+soup_uri_new
+soup_uri_new_with_base
+soup_uri_normalize
+soup_uri_set_fragment
+soup_uri_set_host
+soup_uri_set_password
+soup_uri_set_path
+soup_uri_set_port
+soup_uri_set_query
+soup_uri_set_query_from_fields
+soup_uri_set_query_from_form
+soup_uri_set_scheme
+soup_uri_set_user
+soup_uri_to_string
+soup_uri_uses_default_port
+soup_value_array_append
+soup_value_array_append_vals
+soup_value_array_from_args
+soup_value_array_get_nth
+soup_value_array_insert
+soup_value_array_new
+soup_value_array_new_with_vals
+soup_value_array_to_args
+soup_value_hash_insert
+soup_value_hash_insert_vals
+soup_value_hash_insert_value
+soup_value_hash_lookup
+soup_value_hash_lookup_vals
+soup_value_hash_new
+soup_value_hash_new_with_vals
+soup_xmlrpc_build_fault
+soup_xmlrpc_build_method_call
+soup_xmlrpc_build_method_response
+soup_xmlrpc_error_get_type
+soup_xmlrpc_error_quark
+soup_xmlrpc_extract_method_call
+soup_xmlrpc_extract_method_response
+soup_xmlrpc_fault_get_type
+soup_xmlrpc_fault_quark
+soup_xmlrpc_parse_method_call
+soup_xmlrpc_parse_method_response
+soup_xmlrpc_request_new
+soup_xmlrpc_set_fault
+soup_xmlrpc_set_response
+_SOUP_METHOD_CONNECT
+_SOUP_METHOD_COPY
+_SOUP_METHOD_DELETE
+_SOUP_METHOD_GET
+_SOUP_METHOD_HEAD
+_SOUP_METHOD_LOCK
+_SOUP_METHOD_MKCOL
+_SOUP_METHOD_MOVE
+_SOUP_METHOD_OPTIONS
+_SOUP_METHOD_POST
+_SOUP_METHOD_PROPFIND
+_SOUP_METHOD_PROPPATCH
+_SOUP_METHOD_PUT
+_SOUP_METHOD_TRACE
+_SOUP_METHOD_UNLOCK
+_SOUP_URI_SCHEME_DATA
+_SOUP_URI_SCHEME_FILE
+_SOUP_URI_SCHEME_FTP
+_SOUP_URI_SCHEME_HTTP
+_SOUP_URI_SCHEME_HTTPS
+_SOUP_URI_SCHEME_RESOURCE
diff --git a/libsoup/libsoup-gnome-2.4.sym b/libsoup/libsoup-gnome-2.4.sym
new file mode 100644
index 00000000..67f62dec
--- /dev/null
+++ b/libsoup/libsoup-gnome-2.4.sym
@@ -0,0 +1,5 @@
+soup_cookie_jar_sqlite_get_type
+soup_cookie_jar_sqlite_new
+soup_gnome_features_2_26_get_type
+soup_password_manager_gnome_get_type
+soup_proxy_resolver_gnome_get_type
diff --git a/libsoup/soup-address.c b/libsoup/soup-address.c
index f2e698bc..b2d1647a 100644
--- a/libsoup/soup-address.c
+++ b/libsoup/soup-address.c
@@ -11,9 +11,11 @@
#include <string.h>
+#include <gio/gnetworking.h>
+
#include "soup-address.h"
#include "soup.h"
-#include "soup-marshal.h"
+#include "soup-misc-private.h"
/**
* SECTION:soup-address
@@ -22,6 +24,10 @@
* #SoupAddress represents the address of a TCP connection endpoint:
* both the IP address and the port. (It is somewhat like an
* object-oriented version of struct sockaddr.)
+ *
+ * Although #SoupAddress is still used in some libsoup API's, it
+ * should not be used in new code; use GLib's #GNetworkAddress or
+ * #GSocketAddress instead.
**/
enum {
@@ -46,7 +52,6 @@ typedef struct {
const char *protocol;
GMutex lock;
- GSList *async_lookups;
} SoupAddressPrivate;
#define SOUP_ADDRESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_ADDRESS, SoupAddressPrivate))
@@ -555,6 +560,59 @@ soup_address_get_port (SoupAddress *addr)
}
+/* Tries to resolve priv->name as an IP address, possibly including an
+ * IPv6 scope id.
+ */
+static void
+maybe_resolve_ip (SoupAddress *addr)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ const char *pct, *ip;
+ char *tmp = NULL;
+ GSocketConnectable *gaddr;
+ GSocketAddressEnumerator *sa_enum;
+ GSocketAddress *saddr;
+
+ if (priv->sockaddr || !priv->name)
+ return;
+
+ pct = strchr (priv->name, '%');
+ if (pct)
+ ip = tmp = g_strndup (priv->name, pct - priv->name);
+ else
+ ip = priv->name;
+
+ if (!g_hostname_is_ip_address (ip)) {
+ g_free (tmp);
+ return;
+ }
+ g_free (tmp);
+
+ gaddr = g_network_address_new (priv->name, priv->port);
+ if (!gaddr)
+ return;
+
+ sa_enum = g_socket_connectable_enumerate (gaddr);
+ saddr = g_socket_address_enumerator_next (sa_enum, NULL, NULL);
+ if (saddr) {
+ priv->n_addrs = 1;
+ priv->sockaddr = g_new (struct sockaddr_storage, 1);
+ if (!g_socket_address_to_native (saddr, priv->sockaddr,
+ sizeof (struct sockaddr_storage),
+ NULL)) {
+ /* can't happen: We know the address format is supported
+ * and the buffer is large enough
+ */
+ g_warn_if_reached ();
+ }
+ g_object_unref (saddr);
+ }
+
+ g_object_unref (sa_enum);
+ g_object_unref (gaddr);
+}
+
+
static guint
update_addrs (SoupAddress *addr, GList *addrs, GError *error)
{
@@ -615,55 +673,47 @@ update_name (SoupAddress *addr, const char *name, GError *error)
}
typedef struct {
+ SoupAddress *addr;
SoupAddressCallback callback;
gpointer callback_data;
} SoupAddressResolveAsyncData;
static void
-complete_resolve_async (SoupAddress *addr, guint status)
+complete_resolve_async (SoupAddressResolveAsyncData *res_data, guint status)
{
- SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
- SoupAddressResolveAsyncData *res_data;
- GSList *lookups, *l;
GSource *current_source;
GMainContext *current_context;
- lookups = priv->async_lookups;
- priv->async_lookups = NULL;
-
- /* Awful hack; to make soup_socket_connect_async() with an
- * non-default async_context work correctly, we need to ensure
- * that the non-default context (which we're now running in)
- * is the thread-default when the callbacks are run...
- */
- current_source = g_main_current_source ();
- if (current_source && !g_source_is_destroyed (current_source))
- current_context = g_source_get_context (current_source);
- else
- current_context = NULL;
- g_main_context_push_thread_default (current_context);
+ if (res_data->callback) {
+ /* Awful hack; to make soup_socket_connect_async()
+ * with an non-default async_context work correctly,
+ * we need to ensure that the non-default context
+ * (which we're now running in) is the thread-default
+ * when the callbacks are run...
+ */
+ current_source = g_main_current_source ();
+ if (current_source && !g_source_is_destroyed (current_source))
+ current_context = g_source_get_context (current_source);
+ else
+ current_context = NULL;
+ g_main_context_push_thread_default (current_context);
- for (l = lookups; l; l = l->next) {
- res_data = l->data;
+ res_data->callback (res_data->addr, status,
+ res_data->callback_data);
- if (res_data->callback) {
- res_data->callback (addr, status,
- res_data->callback_data);
- }
- g_slice_free (SoupAddressResolveAsyncData, res_data);
+ g_main_context_pop_thread_default (current_context);
}
- g_slist_free (lookups);
- g_main_context_pop_thread_default (current_context);
-
- g_object_unref (addr);
+ g_object_unref (res_data->addr);
+ g_slice_free (SoupAddressResolveAsyncData, res_data);
}
static void
lookup_resolved (GObject *source, GAsyncResult *result, gpointer user_data)
{
GResolver *resolver = G_RESOLVER (source);
- SoupAddress *addr = user_data;
+ SoupAddressResolveAsyncData *res_data = user_data;
+ SoupAddress *addr = res_data->addr;
SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
GError *error = NULL;
guint status;
@@ -689,7 +739,7 @@ lookup_resolved (GObject *source, GAsyncResult *result, gpointer user_data)
g_object_ref (addr);
g_object_set_data (G_OBJECT (addr), "async-resolved-error", error);
- complete_resolve_async (addr, status);
+ complete_resolve_async (res_data, status);
g_object_set_data (G_OBJECT (addr), "async-resolved-error", NULL);
g_object_unref (addr);
@@ -698,9 +748,9 @@ lookup_resolved (GObject *source, GAsyncResult *result, gpointer user_data)
}
static gboolean
-idle_complete_resolve (gpointer addr)
+idle_complete_resolve (gpointer res_data)
{
- complete_resolve_async (addr, SOUP_STATUS_OK);
+ complete_resolve_async (res_data, SOUP_STATUS_OK);
return FALSE;
}
@@ -746,7 +796,6 @@ soup_address_resolve_async (SoupAddress *addr, GMainContext *async_context,
SoupAddressPrivate *priv;
SoupAddressResolveAsyncData *res_data;
GResolver *resolver;
- gboolean already_started;
g_return_if_fail (SOUP_IS_ADDRESS (addr));
priv = SOUP_ADDRESS_GET_PRIVATE (addr);
@@ -756,47 +805,43 @@ soup_address_resolve_async (SoupAddress *addr, GMainContext *async_context,
* not intended to be thread-safe.
*/
+ if (priv->name && !priv->sockaddr)
+ maybe_resolve_ip (addr);
if (priv->name && priv->sockaddr && !callback)
return;
res_data = g_slice_new0 (SoupAddressResolveAsyncData);
+ res_data->addr = g_object_ref (addr);
res_data->callback = callback;
res_data->callback_data = user_data;
- already_started = priv->async_lookups != NULL;
- priv->async_lookups = g_slist_prepend (priv->async_lookups, res_data);
-
- if (already_started)
- return;
-
- g_object_ref (addr);
-
- if (priv->name && priv->sockaddr) {
- soup_add_completion (async_context, idle_complete_resolve, addr);
- return;
- }
-
- resolver = g_resolver_get_default ();
if (async_context)
g_main_context_push_thread_default (async_context);
- if (priv->name) {
- g_resolver_lookup_by_name_async (resolver, priv->name,
- cancellable,
- lookup_resolved, addr);
- } else {
- GInetAddress *gia;
+ if (priv->name && priv->sockaddr)
+ soup_add_completion (async_context, idle_complete_resolve, res_data);
+ else {
+ resolver = g_resolver_get_default ();
+
+ if (priv->name) {
+ g_resolver_lookup_by_name_async (resolver, priv->name,
+ cancellable,
+ lookup_resolved, res_data);
+ } else {
+ GInetAddress *gia;
+
+ gia = soup_address_make_inet_address (addr);
+ g_resolver_lookup_by_address_async (resolver, gia,
+ cancellable,
+ lookup_resolved, res_data);
+ g_object_unref (gia);
+ }
- gia = soup_address_make_inet_address (addr);
- g_resolver_lookup_by_address_async (resolver, gia,
- cancellable,
- lookup_resolved, addr);
- g_object_unref (gia);
+ g_object_unref (resolver);
}
if (async_context)
g_main_context_pop_thread_default (async_context);
- g_object_unref (resolver);
}
static guint
@@ -816,6 +861,10 @@ resolve_sync_internal (SoupAddress *addr, GCancellable *cancellable, GError **er
* blocking op, and then re-lock it to modify @addr.
*/
g_mutex_lock (&priv->lock);
+
+ if (priv->name && !priv->sockaddr)
+ maybe_resolve_ip (addr);
+
if (!priv->sockaddr) {
GList *addrs;
@@ -841,6 +890,7 @@ resolve_sync_internal (SoupAddress *addr, GCancellable *cancellable, GError **er
g_free (name);
} else
status = SOUP_STATUS_OK;
+
g_mutex_unlock (&priv->lock);
if (my_err)
@@ -1111,15 +1161,19 @@ soup_address_address_enumerator_next (GSocketAddressEnumerator *enumerator,
static void
got_addresses (SoupAddress *addr, guint status, gpointer user_data)
{
- GSimpleAsyncResult *simple = user_data;
+ GTask *task = user_data;
GError *error;
error = g_object_get_data (G_OBJECT (addr), "async-resolved-error");
if (error)
- g_simple_async_result_set_from_error (simple, error);
+ g_task_return_error (task, g_error_copy (error));
+ else {
+ GSocketAddress *addr;
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ addr = next_address (g_task_get_source_object (task));
+ g_task_return_pointer (task, addr, g_object_unref);
+ }
+ g_object_unref (task);
}
static void
@@ -1131,18 +1185,16 @@ soup_address_address_enumerator_next_async (GSocketAddressEnumerator *enumerato
SoupAddressAddressEnumerator *addr_enum =
SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
- GSimpleAsyncResult *simple;
-
- simple = g_simple_async_result_new (G_OBJECT (enumerator),
- callback, user_data,
- soup_address_address_enumerator_next_async);
+ GTask *task;
+ task = g_task_new (enumerator, cancellable, callback, user_data);
if (!priv->sockaddr) {
- soup_address_resolve_async (addr_enum->addr, NULL, cancellable,
- got_addresses, simple);
+ soup_address_resolve_async (addr_enum->addr,
+ g_main_context_get_thread_default (),
+ cancellable, got_addresses, task);
} else {
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, next_address (addr_enum), g_object_unref);
+ g_object_unref (task);
}
}
@@ -1151,14 +1203,7 @@ soup_address_address_enumerator_next_finish (GSocketAddressEnumerator *enumerat
GAsyncResult *result,
GError **error)
{
- SoupAddressAddressEnumerator *addr_enum =
- SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
-
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
- else
- return next_address (addr_enum);
+ return g_task_propagate_pointer (G_TASK (result), error);
}
static void
@@ -1200,20 +1245,25 @@ soup_address_connectable_proxy_enumerate (GSocketConnectable *connectable)
SoupAddress *addr = SOUP_ADDRESS (connectable);
SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
GSocketAddressEnumerator *proxy_enum;
- char *uri;
+ SoupURI *uri;
+ char *uri_string;
/* We cheerily assume "http" here because you shouldn't be
* using SoupAddress any more if you're not doing HTTP anyway.
*/
- uri = g_strdup_printf ("%s://%s:%u",
- priv->protocol ? priv->protocol : "http",
- priv->name ? priv->name : soup_address_get_physical (addr),
- priv->port);
+ uri = soup_uri_new (NULL);
+ soup_uri_set_scheme (uri, priv->protocol ? priv->protocol : "http");
+ soup_uri_set_host (uri, priv->name ? priv->name : soup_address_get_physical (addr));
+ soup_uri_set_port (uri, priv->port);
+ soup_uri_set_path (uri, "");
+ uri_string = soup_uri_to_string_internal (uri, FALSE, TRUE);
+
proxy_enum = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
"connectable", connectable,
- "uri", uri,
+ "uri", uri_string,
NULL);
- g_free (uri);
+ g_free (uri_string);
+ soup_uri_free (uri);
return proxy_enum;
}
diff --git a/libsoup/soup-address.h b/libsoup/soup-address.h
index 579d1d3c..797a5506 100644
--- a/libsoup/soup-address.h
+++ b/libsoup/soup-address.h
@@ -8,7 +8,6 @@
#include <sys/types.h>
-#include <libsoup/soup-portability.h>
#include <libsoup/soup-types.h>
G_BEGIN_DECLS
@@ -51,6 +50,8 @@ typedef enum {
#define SOUP_ADDRESS_ANY_PORT 0
+struct sockaddr;
+
typedef void (*SoupAddressCallback) (SoupAddress *addr,
guint status,
gpointer user_data);
@@ -77,13 +78,18 @@ const char *soup_address_get_physical (SoupAddress *addr);
guint soup_address_get_port (SoupAddress *addr);
struct sockaddr *soup_address_get_sockaddr (SoupAddress *addr,
int *len);
+SOUP_AVAILABLE_IN_2_32
GSocketAddress *soup_address_get_gsockaddr (SoupAddress *addr);
gboolean soup_address_is_resolved (SoupAddress *addr);
+SOUP_AVAILABLE_IN_2_26
guint soup_address_hash_by_name (gconstpointer addr);
+SOUP_AVAILABLE_IN_2_26
gboolean soup_address_equal_by_name (gconstpointer addr1,
gconstpointer addr2);
+SOUP_AVAILABLE_IN_2_26
guint soup_address_hash_by_ip (gconstpointer addr);
+SOUP_AVAILABLE_IN_2_26
gboolean soup_address_equal_by_ip (gconstpointer addr1,
gconstpointer addr2);
diff --git a/libsoup/soup-auth-basic.c b/libsoup/soup-auth-basic.c
index fb491c71..f298e36c 100644
--- a/libsoup/soup-auth-basic.c
+++ b/libsoup/soup-auth-basic.c
@@ -19,6 +19,17 @@ typedef struct {
} SoupAuthBasicPrivate;
#define SOUP_AUTH_BASIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_BASIC, SoupAuthBasicPrivate))
+/**
+ * SOUP_TYPE_AUTH_BASIC:
+ *
+ * A #GType corresponding to HTTP "Basic" authentication.
+ * #SoupSessions support this by default; if you want to disable
+ * support for it, call soup_session_remove_feature_by_type(),
+ * passing %SOUP_TYPE_AUTH_BASIC.
+ *
+ * Since: 2.34
+ */
+
G_DEFINE_TYPE (SoupAuthBasic, soup_auth_basic, SOUP_TYPE_AUTH)
static void
@@ -62,7 +73,7 @@ soup_auth_basic_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
space = g_strdup (source_uri->path);
- /* Strip query and filename component */
+ /* Strip filename component */
p = strrchr (space, '/');
if (p && p != space && p[1])
*p = '\0';
diff --git a/libsoup/soup-auth-digest.c b/libsoup/soup-auth-digest.c
index 5fe75610..1fbb639d 100644
--- a/libsoup/soup-auth-digest.c
+++ b/libsoup/soup-auth-digest.c
@@ -15,6 +15,10 @@
#include "soup.h"
#include "soup-message-private.h"
+#ifdef G_OS_WIN32
+#include <process.h>
+#endif
+
typedef struct {
char *user;
char hex_urp[33];
@@ -36,6 +40,17 @@ typedef struct {
static void recompute_hex_a1 (SoupAuthDigestPrivate *priv);
+/**
+ * SOUP_TYPE_AUTH_DIGEST:
+ *
+ * A #GType corresponding to HTTP "Digest" authentication.
+ * #SoupSessions support this by default; if you want to disable
+ * support for it, call soup_session_remove_feature_by_type(),
+ * passing %SOUP_TYPE_AUTH_DIGEST.
+ *
+ * Since: 2.34
+ */
+
G_DEFINE_TYPE (SoupAuthDigest, soup_auth_digest, SOUP_TYPE_AUTH)
static void
@@ -278,6 +293,9 @@ soup_auth_digest_authenticate (SoupAuth *auth, const char *username,
SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
char *bgen;
+ g_clear_pointer (&priv->cnonce, g_free);
+ g_clear_pointer (&priv->user, g_free);
+
/* Create client nonce */
bgen = g_strdup_printf ("%p:%lu:%lu",
auth,
diff --git a/libsoup/soup-auth-domain-basic.c b/libsoup/soup-auth-domain-basic.c
index 9e8afe8f..b843ad72 100644
--- a/libsoup/soup-auth-domain-basic.c
+++ b/libsoup/soup-auth-domain-basic.c
@@ -13,7 +13,6 @@
#include "soup-auth-domain-basic.h"
#include "soup.h"
-#include "soup-marshal.h"
/**
* SECTION:soup-auth-domain-basic
diff --git a/libsoup/soup-auth-domain-digest.c b/libsoup/soup-auth-domain-digest.c
index 985919f4..ebf3ddb6 100644
--- a/libsoup/soup-auth-domain-digest.c
+++ b/libsoup/soup-auth-domain-digest.c
@@ -15,13 +15,12 @@
#include "soup-auth-domain-digest.h"
#include "soup.h"
#include "soup-auth-digest.h"
-#include "soup-marshal.h"
/**
* SECTION:soup-auth-domain-digest
* @short_description: Server-side "Digest" authentication
*
- * #SoupAuthDomainBasic handles the server side of HTTP "Digest"
+ * #SoupAuthDomainDigest handles the server side of HTTP "Digest"
* authentication.
**/
diff --git a/libsoup/soup-auth-manager-ntlm.c b/libsoup/soup-auth-manager-ntlm.c
deleted file mode 100644
index f2b67d1d..00000000
--- a/libsoup/soup-auth-manager-ntlm.c
+++ /dev/null
@@ -1,1360 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-auth-manager-ntlm.c: NTLM auth manager
- *
- * Copyright (C) 2001-2007 Novell, Inc.
- * Copyright (C) 2008 Red Hat, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "soup-auth-manager-ntlm.h"
-#include "soup.h"
-#include "soup-auth-ntlm.h"
-#include "soup-message-private.h"
-
-static void soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
-static SoupSessionFeatureInterface *soup_auth_manager_parent_feature_interface;
-
-G_DEFINE_TYPE_WITH_CODE (SoupAuthManagerNTLM, soup_auth_manager_ntlm, SOUP_TYPE_AUTH_MANAGER,
- G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
- soup_auth_manager_ntlm_session_feature_init))
-
-typedef enum {
- SOUP_NTLM_NEW,
-#ifdef USE_NTLM_AUTH
- SOUP_NTLM_SENT_SSO_REQUEST,
- SOUP_NTLM_RECEIVED_SSO_CHALLENGE,
- SOUP_NTLM_SENT_SSO_RESPONSE,
- SOUP_NTLM_SSO_UNAVAILABLE,
- SOUP_NTLM_SSO_FAILED,
-#endif
- SOUP_NTLM_SENT_REQUEST,
- SOUP_NTLM_RECEIVED_CHALLENGE,
- SOUP_NTLM_SENT_RESPONSE,
- SOUP_NTLM_FAILED
-} SoupNTLMState;
-
-typedef struct {
- SoupSocket *socket;
- SoupNTLMState state;
- char *response_header;
-
- char *nonce, *domain;
- SoupAuth *auth;
-#ifdef USE_NTLM_AUTH
- char *challenge_header;
- int fd_in;
- int fd_out;
-#endif
-} SoupNTLMConnection;
-
-static void free_ntlm_connection (SoupNTLMConnection *conn);
-
-typedef struct {
- gboolean use_ntlm;
-
- SoupSession *session;
- GHashTable *connections_by_msg;
- GHashTable *connections_by_id;
-#ifdef USE_NTLM_AUTH
- gboolean ntlm_auth_accessible;
-#endif
-} SoupAuthManagerNTLMPrivate;
-#define SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMPrivate))
-
-static char *soup_ntlm_request (void);
-static gboolean soup_ntlm_parse_challenge (const char *challenge,
- char **nonce,
- char **default_domain);
-static char *soup_ntlm_response (const char *nonce,
- const char *user,
- const char *password,
- const char *host,
- const char *domain);
-#ifdef USE_NTLM_AUTH
-static void sso_ntlm_close (SoupNTLMConnection *conn);
-#endif
-
-static void
-soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
-
- priv->connections_by_id = g_hash_table_new_full (NULL, NULL, NULL,
- (GDestroyNotify)free_ntlm_connection);
- priv->connections_by_msg = g_hash_table_new (NULL, NULL);
-#ifdef USE_NTLM_AUTH
- priv->ntlm_auth_accessible = (access (NTLM_AUTH, X_OK) == 0);
-#endif
-}
-
-static void
-free_ntlm_connection (SoupNTLMConnection *conn)
-{
- g_free (conn->response_header);
- g_free (conn->nonce);
- g_free (conn->domain);
- g_clear_object (&conn->auth);
-#ifdef USE_NTLM_AUTH
- g_free (conn->challenge_header);
- sso_ntlm_close (conn);
-#endif
- g_slice_free (SoupNTLMConnection, conn);
-}
-
-static void
-soup_auth_manager_ntlm_finalize (GObject *object)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (object);
-
- g_hash_table_destroy (priv->connections_by_id);
- g_hash_table_destroy (priv->connections_by_msg);
-
- G_OBJECT_CLASS (soup_auth_manager_ntlm_parent_class)->finalize (object);
-}
-
-static void
-soup_auth_manager_ntlm_class_init (SoupAuthManagerNTLMClass *auth_manager_ntlm_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (auth_manager_ntlm_class);
-
- g_type_class_add_private (auth_manager_ntlm_class, sizeof (SoupAuthManagerNTLMPrivate));
-
- object_class->finalize = soup_auth_manager_ntlm_finalize;
-}
-
-static void
-soup_auth_manager_ntlm_attach (SoupSessionFeature *manager, SoupSession *session)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (manager);
-
- /* FIXME: should support multiple sessions */
- priv->session = session;
-
- soup_auth_manager_parent_feature_interface->attach (manager, session);
-}
-
-static void
-delete_conn (SoupSocket *socket, gpointer user_data)
-{
- SoupAuthManagerNTLMPrivate *priv = user_data;
-
- g_hash_table_remove (priv->connections_by_id, socket);
- g_signal_handlers_disconnect_by_func (socket, delete_conn, priv);
-}
-
-static SoupNTLMConnection *
-get_connection (SoupAuthManagerNTLMPrivate *priv, SoupSocket *socket)
-{
- SoupNTLMConnection *conn;
-
- conn = g_hash_table_lookup (priv->connections_by_id, socket);
- if (conn)
- return conn;
-
- conn = g_slice_new0 (SoupNTLMConnection);
- conn->socket = socket;
- conn->state = SOUP_NTLM_NEW;
-#ifdef USE_NTLM_AUTH
- conn->fd_in = -1;
- conn->fd_out = -1;
-#endif
- g_hash_table_insert (priv->connections_by_id, socket, conn);
-
- g_signal_connect (socket, "disconnected",
- G_CALLBACK (delete_conn), priv);
- return conn;
-}
-
-static void
-unset_conn (SoupMessage *msg, gpointer user_data)
-{
- SoupAuthManagerNTLMPrivate *priv = user_data;
-
- g_hash_table_remove (priv->connections_by_msg, msg);
- g_signal_handlers_disconnect_by_func (msg, unset_conn, priv);
-}
-
-static SoupNTLMConnection *
-set_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg,
- SoupNTLMConnection *conn)
-{
- if (!g_hash_table_lookup (priv->connections_by_msg, msg)) {
- g_signal_connect (msg, "finished",
- G_CALLBACK (unset_conn), priv);
- g_signal_connect (msg, "restarted",
- G_CALLBACK (unset_conn), priv);
- }
- g_hash_table_insert (priv->connections_by_msg, msg, conn);
-
- return conn;
-}
-
-static SoupNTLMConnection *
-get_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg)
-{
- return g_hash_table_lookup (priv->connections_by_msg, msg);
-}
-
-#ifdef USE_NTLM_AUTH
-static void
-sso_ntlm_close (SoupNTLMConnection *conn)
-{
- if (conn->fd_in != -1) {
- close (conn->fd_in);
- conn->fd_in = -1;
- }
-
- if (conn->fd_out != -1) {
- close (conn->fd_out);
- conn->fd_out = -1;
- }
-}
-
-static gboolean
-sso_ntlm_initiate (SoupNTLMConnection *conn, SoupAuthManagerNTLMPrivate *priv)
-{
- char *username = NULL, *slash, *domain = NULL;
- char *argv[9];
- gboolean ret;
-
- /* Return if ntlm_auth execution process exist already */
- if (conn->fd_in != -1 && conn->fd_out != -1)
- return TRUE;
- else
- /* Clean all sso data before re-initiate */
- sso_ntlm_close (conn);
-
- if (!priv->ntlm_auth_accessible)
- goto done;
-
- username = getenv ("NTLMUSER");
- if (!username)
- username = getenv ("USER");
- if (!username)
- goto done;
-
- slash = strpbrk (username, "\\/");
- if (slash) {
- domain = g_strdup (username);
- slash = domain + (slash - username);
- *slash = '\0';
- username = slash + 1;
- }
-
- argv[0] = NTLM_AUTH;
- argv[1] = "--helper-protocol";
- argv[2] = "ntlmssp-client-1";
- argv[3] = "--use-cached-creds";
- argv[4] = "--username";
- argv[5] = username;
- argv[6] = domain ? "--domain" : NULL;
- argv[7] = domain;
- argv[8] = NULL;
- /* Spawn child process */
- ret = g_spawn_async_with_pipes (NULL, argv, NULL,
- G_SPAWN_FILE_AND_ARGV_ZERO |
- G_SPAWN_STDERR_TO_DEV_NULL,
- NULL, NULL,
- NULL, &conn->fd_in, &conn->fd_out,
- NULL, NULL);
- if (!ret)
- goto done;
- g_free (domain);
- return TRUE;
-done:
- g_free (domain);
- return FALSE;
-}
-
-static char *
-sso_ntlm_response (SoupNTLMConnection *conn, const char *input, SoupNTLMState conn_state)
-{
- ssize_t size;
- char buf[1024], *response = NULL;
- char *tmpbuf = buf;
- size_t len_in = strlen (input), len_out = sizeof (buf);
-
- while (len_in > 0) {
- int written = write (conn->fd_in, input, len_in);
- if (written == -1) {
- /* Interrupted by a signal, retry it */
- if (errno == EINTR)
- continue;
- /* write failed if other errors happen */
- goto done;
- }
- input += written;
- len_in -= written;
- }
- /* Read one line */
- while (len_out > 0) {
- size = read (conn->fd_out, tmpbuf, len_out);
- if (size == -1) {
- if (errno == EINTR)
- continue;
- goto done;
- } else if (size == 0)
- goto done;
- else if (tmpbuf[size - 1] == '\n') {
- tmpbuf[size - 1] = '\0';
- goto wrfinish;
- }
- tmpbuf += size;
- len_out -= size;
- }
- goto done;
-wrfinish:
- if (g_ascii_strcasecmp (buf, "PW") == 0) {
- /* Samba/winbind installed but not configured */
- response = g_strdup ("PW");
- goto done;
- }
- if (conn_state == SOUP_NTLM_NEW &&
- g_ascii_strncasecmp (buf, "YR ", 3) != 0)
- /* invalid response for type 1 message */
- goto done;
- if (conn_state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE &&
- g_ascii_strncasecmp (buf, "KK ", 3) != 0 &&
- g_ascii_strncasecmp (buf, "AF ", 3) != 0)
- /* invalid response for type 3 message */
- goto done;
-
- response = g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
- goto done;
-done:
- return response;
-}
-#endif /* USE_NTLM_AUTH */
-
-static void
-ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
- SoupNTLMConnection *conn;
- const char *val;
- char *challenge = NULL;
- SoupURI *uri;
-
- conn = get_connection_for_msg (priv, msg);
- if (!conn)
- return;
-
- val = soup_message_headers_get_list (msg->response_headers,
- "WWW-Authenticate");
- if (!val)
- return;
- challenge = soup_auth_manager_extract_challenge (val, "NTLM");
- if (!challenge)
- return;
-
- if (conn->state > SOUP_NTLM_SENT_REQUEST) {
- /* We already authenticated, but then got another 401.
- * That means "permission denied", so don't try to
- * authenticate again.
- */
- conn->state = SOUP_NTLM_FAILED;
- goto done;
- }
-
- if (!soup_ntlm_parse_challenge (challenge, &conn->nonce, &conn->domain)) {
- conn->state = SOUP_NTLM_FAILED;
- goto done;
- }
-
- conn->auth = soup_auth_ntlm_new (conn->domain,
- soup_message_get_uri (msg)->host);
-#ifdef USE_NTLM_AUTH
- conn->challenge_header = g_strdup (challenge + 5);
- if (conn->state == SOUP_NTLM_SENT_SSO_REQUEST) {
- conn->state = SOUP_NTLM_RECEIVED_SSO_CHALLENGE;
- goto done;
- }
-#endif
- conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
-
- uri = soup_message_get_uri (msg);
- if (uri->password)
- soup_auth_authenticate (conn->auth, uri->user, uri->password);
- else {
- soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (ntlm),
- msg, conn->auth, FALSE);
- }
-
- done:
- g_free (challenge);
-
- /* Remove the WWW-Authenticate headers so the session won't try
- * to do Basic auth too.
- */
- soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
-}
-
-static void
-ntlm_authorize_post (SoupMessage *msg, gpointer ntlm)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
- SoupNTLMConnection *conn;
- const char *username = NULL, *password = NULL;
- char *slash, *domain = NULL;
- SoupMessageFlags flags;
-
- conn = get_connection_for_msg (priv, msg);
- if (!conn || !conn->auth)
- return;
-
-#ifdef USE_NTLM_AUTH
- if (conn->state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE) {
- char *input, *header;
- input = g_strdup_printf ("TT %s\n", conn->challenge_header);
- /* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
- if (sso_ntlm_initiate (conn, priv)) {
- header = sso_ntlm_response (conn, input, conn->state);
- g_free (input);
- /* Close ntlm_auth as it is no longer needed for current connection */
- sso_ntlm_close (conn);
- if (!header) {
- conn->state = SOUP_NTLM_SSO_FAILED;
- g_free (header);
- goto ssofailure;
- }
- if (!g_ascii_strcasecmp (header, "PW")) {
- conn->state = SOUP_NTLM_SSO_UNAVAILABLE;
- g_free (header);
- goto ssofailure;
- }
-
- conn->response_header = header;
- soup_session_requeue_message (priv->session, msg);
- goto done;
- }
- conn->state = SOUP_NTLM_SSO_FAILED;
-ssofailure:
- soup_session_requeue_message (priv->session, msg);
- goto done;
- }
-#endif
- username = soup_auth_ntlm_get_username (conn->auth);
- password = soup_auth_ntlm_get_password (conn->auth);
- if (!username || !password)
- goto done;
-
- slash = strpbrk (username, "\\/");
- if (slash) {
- domain = g_strdup (username);
- slash = domain + (slash - username);
- *slash = '\0';
- username = slash + 1;
- } else
- domain = conn->domain;
-
- conn->response_header = soup_ntlm_response (conn->nonce,
- username, password,
- NULL, domain);
-
- flags = soup_message_get_flags (msg);
- soup_message_set_flags (msg, flags & ~SOUP_MESSAGE_NEW_CONNECTION);
- soup_session_requeue_message (priv->session, msg);
-
-done:
- if (domain != conn->domain)
- g_free (domain);
- g_free (conn->domain);
- conn->domain = NULL;
- g_free (conn->nonce);
- conn->nonce = NULL;
- g_object_unref (conn->auth);
- conn->auth = NULL;
-}
-
-static void
-soup_auth_manager_ntlm_request_queued (SoupSessionFeature *ntlm,
- SoupSession *session,
- SoupMessage *msg)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
-
- if (priv->use_ntlm) {
- soup_message_add_status_code_handler (
- msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
- G_CALLBACK (ntlm_authorize_pre), ntlm);
- soup_message_add_status_code_handler (
- msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
- G_CALLBACK (ntlm_authorize_post), ntlm);
- }
-
- soup_auth_manager_parent_feature_interface->request_queued (ntlm, session, msg);
-}
-
-static void
-soup_auth_manager_ntlm_request_started (SoupSessionFeature *ntlm,
- SoupSession *session,
- SoupMessage *msg,
- SoupSocket *socket)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (ntlm);
- SoupNTLMConnection *conn;
- char *header = NULL;
-
- if (!priv->use_ntlm)
- goto super;
-
- conn = get_connection (priv, socket);
- set_connection_for_msg (priv, msg, conn);
-
- switch (conn->state) {
- case SOUP_NTLM_NEW:
-#ifdef USE_NTLM_AUTH
- /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
- * by delegating the NTLM challenge/response protocal to a helper
- * in ntlm_auth.
- * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
- * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
- * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
- * The preprocessor variable 'USE_NTLM_AUTH' indicates whether
- * this feature is enabled. Another one 'NTLM_AUTH' contains absolute
- * path of it.
- * If NTLM single-sign-on fails, go back to original request handling process.
- */
- if (sso_ntlm_initiate (conn, priv)) {
- header = sso_ntlm_response (conn, "YR\n", conn->state);
- if (header) {
- if (g_ascii_strcasecmp (header, "PW") != 0) {
- conn->state = SOUP_NTLM_SENT_SSO_REQUEST;
- break;
- } else {
- g_free (header);
- header = NULL;
- goto ssounavailable;
- }
- } else {
- g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
- goto ssounavailable;
- }
- }
- case SOUP_NTLM_SSO_UNAVAILABLE:
- ssounavailable:
-#endif
- header = soup_ntlm_request ();
- conn->state = SOUP_NTLM_SENT_REQUEST;
- break;
-#ifdef USE_NTLM_AUTH
- case SOUP_NTLM_RECEIVED_SSO_CHALLENGE:
- header = conn->response_header;
- conn->response_header = NULL;
- conn->state = SOUP_NTLM_SENT_SSO_RESPONSE;
- break;
- case SOUP_NTLM_SSO_FAILED:
- /* Restart request without SSO */
- g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
- header = soup_ntlm_request ();
- conn->state = SOUP_NTLM_SENT_REQUEST;
- break;
-#endif
- case SOUP_NTLM_RECEIVED_CHALLENGE:
- header = conn->response_header;
- conn->response_header = NULL;
- conn->state = SOUP_NTLM_SENT_RESPONSE;
- break;
- default:
- break;
- }
-
- if (header && !soup_message_get_auth (msg)) {
- soup_message_headers_replace (msg->request_headers,
- "Authorization", header);
- g_free (header);
- }
-
-super:
- soup_auth_manager_parent_feature_interface->request_started (ntlm, session, msg, socket);
-}
-
-static void
-soup_auth_manager_ntlm_request_unqueued (SoupSessionFeature *ntlm,
- SoupSession *session,
- SoupMessage *msg)
-{
- g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
- g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
-
- soup_auth_manager_parent_feature_interface->request_unqueued (ntlm, session, msg);
-}
-
-static gboolean
-soup_auth_manager_ntlm_add_feature (SoupSessionFeature *feature, GType type)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
-
- if (type == SOUP_TYPE_AUTH_NTLM) {
- priv->use_ntlm = TRUE;
- return TRUE;
- }
-
- return soup_auth_manager_parent_feature_interface->add_feature (feature, type);
-}
-
-static gboolean
-soup_auth_manager_ntlm_remove_feature (SoupSessionFeature *feature, GType type)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
-
- if (type == SOUP_TYPE_AUTH_NTLM) {
- priv->use_ntlm = FALSE;
- return TRUE;
- }
-
- return soup_auth_manager_parent_feature_interface->remove_feature (feature, type);
-}
-
-static gboolean
-soup_auth_manager_ntlm_has_feature (SoupSessionFeature *feature, GType type)
-{
- SoupAuthManagerNTLMPrivate *priv =
- SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE (feature);
-
- if (type == SOUP_TYPE_AUTH_NTLM)
- return priv->use_ntlm;
-
- return soup_auth_manager_parent_feature_interface->has_feature (feature, type);
-}
-
-static void
-soup_auth_manager_ntlm_session_feature_init (SoupSessionFeatureInterface *feature_interface,
- gpointer interface_data)
-{
- soup_auth_manager_parent_feature_interface =
- g_type_interface_peek_parent (feature_interface);
-
- feature_interface->attach = soup_auth_manager_ntlm_attach;
- feature_interface->request_queued = soup_auth_manager_ntlm_request_queued;
- feature_interface->request_started = soup_auth_manager_ntlm_request_started;
- feature_interface->request_unqueued = soup_auth_manager_ntlm_request_unqueued;
- feature_interface->add_feature = soup_auth_manager_ntlm_add_feature;
- feature_interface->remove_feature = soup_auth_manager_ntlm_remove_feature;
- feature_interface->has_feature = soup_auth_manager_ntlm_has_feature;
-}
-
-/* NTLM code */
-
-static void md4sum (const unsigned char *in,
- int nbytes,
- unsigned char digest[16]);
-
-typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */
-
-static void deskey (DES_KS, unsigned char *, int);
-
-static void des (DES_KS, unsigned char *);
-
-static void setup_schedule (const guchar *key_56, DES_KS ks);
-
-static void calc_response (const guchar *key,
- const guchar *plaintext,
- guchar *results);
-
-#define LM_PASSWORD_MAGIC "\x4B\x47\x53\x21\x40\x23\x24\x25" \
- "\x4B\x47\x53\x21\x40\x23\x24\x25" \
- "\x00\x00\x00\x00\x00"
-
-static void
-lanmanager_hash (const char *password, guchar hash[21])
-{
- guchar lm_password [15];
- DES_KS ks;
- int i;
-
- for (i = 0; i < 14 && password [i]; i++)
- lm_password [i] = toupper ((unsigned char) password [i]);
-
- for (; i < 15; i++)
- lm_password [i] = '\0';
-
- memcpy (hash, LM_PASSWORD_MAGIC, 21);
-
- setup_schedule (lm_password, ks);
- des (ks, hash);
-
- setup_schedule (lm_password + 7, ks);
- des (ks, hash + 8);
-}
-
-static void
-nt_hash (const char *password, guchar hash[21])
-{
- unsigned char *buf, *p;
-
- p = buf = g_malloc (strlen (password) * 2);
-
- while (*password) {
- *p++ = *password++;
- *p++ = '\0';
- }
-
- md4sum (buf, p - buf, hash);
- memset (hash + 16, 0, 5);
-
- g_free (buf);
-}
-
-typedef struct {
- guint16 length;
- guint16 length2;
- guint16 offset;
- guchar zero_pad[2];
-} NTLMString;
-
-#define NTLM_CHALLENGE_NONCE_OFFSET 24
-#define NTLM_CHALLENGE_NONCE_LENGTH 8
-#define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
-
-#define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
-#define NTLM_RESPONSE_FLAGS 0x8201
-
-typedef struct {
- guchar header[12];
-
- NTLMString lm_resp;
- NTLMString nt_resp;
- NTLMString domain;
- NTLMString user;
- NTLMString host;
- NTLMString session_key;
-
- guint32 flags;
-} NTLMResponse;
-
-static void
-ntlm_set_string (NTLMString *string, int *offset, int len)
-{
- string->offset = GUINT16_TO_LE (*offset);
- string->length = string->length2 = GUINT16_TO_LE (len);
- *offset += len;
-}
-
-static char *
-soup_ntlm_request (void)
-{
- return g_strdup ("NTLM TlRMTVNTUAABAAAABYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
-}
-
-static gboolean
-soup_ntlm_parse_challenge (const char *challenge,
- char **nonce,
- char **default_domain)
-{
- gsize clen;
- NTLMString domain;
- guchar *chall;
-
- if (strncmp (challenge, "NTLM ", 5) != 0)
- return FALSE;
-
- chall = g_base64_decode (challenge + 5, &clen);
- if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
- clen < NTLM_CHALLENGE_NONCE_OFFSET + NTLM_CHALLENGE_NONCE_LENGTH) {
- g_free (chall);
- return FALSE;
- }
-
- if (default_domain) {
- memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
- domain.length = GUINT16_FROM_LE (domain.length);
- domain.offset = GUINT16_FROM_LE (domain.offset);
-
- if (clen < domain.length + domain.offset) {
- g_free (chall);
- return FALSE;
- }
-
- *default_domain = g_convert ((char *)chall + domain.offset,
- domain.length, "UTF-8", "UCS-2LE",
- NULL, NULL, NULL);
- }
-
- if (nonce) {
- *nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
- NTLM_CHALLENGE_NONCE_LENGTH);
- }
-
- g_free (chall);
- return TRUE;
-}
-
-static char *
-soup_ntlm_response (const char *nonce,
- const char *user,
- const char *password,
- const char *host,
- const char *domain)
-{
- int offset;
- gsize hlen, dlen, ulen;
- guchar hash[21], lm_resp[24], nt_resp[24];
- char *user_conv, *host_conv, *domain_conv;
- NTLMResponse resp;
- char *out, *p;
- int state, save;
-
- nt_hash (password, hash);
- calc_response (hash, (guchar *)nonce, nt_resp);
- lanmanager_hash (password, hash);
- calc_response (hash, (guchar *)nonce, lm_resp);
-
- memset (&resp, 0, sizeof (resp));
- memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
- resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
-
- offset = sizeof (resp);
-
- if (!host)
- host = "UNKNOWN";
-
- domain_conv = g_convert (domain, -1, "UCS-2LE", "UTF-8", NULL, &dlen, NULL);
- user_conv = g_convert (user, -1, "UCS-2LE", "UTF-8", NULL, &ulen, NULL);
- host_conv = g_convert (host, -1, "UCS-2LE", "UTF-8", NULL, &hlen, NULL);
-
- ntlm_set_string (&resp.domain, &offset, dlen);
- ntlm_set_string (&resp.user, &offset, ulen);
- ntlm_set_string (&resp.host, &offset, hlen);
- ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
- ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
-
- out = g_malloc (((offset + 3) * 4) / 3 + 6);
- strncpy (out, "NTLM ", 5);
- p = out + 5;
-
- state = save = 0;
-
- p += g_base64_encode_step ((const guchar *) &resp, sizeof (resp),
- FALSE, p, &state, &save);
- p += g_base64_encode_step ((const guchar *) domain_conv, dlen,
- FALSE, p, &state, &save);
- p += g_base64_encode_step ((const guchar *) user_conv, ulen,
- FALSE, p, &state, &save);
- p += g_base64_encode_step ((const guchar *) host_conv, hlen,
- FALSE, p, &state, &save);
- p += g_base64_encode_step (lm_resp, sizeof (lm_resp),
- FALSE, p, &state, &save);
- p += g_base64_encode_step (nt_resp, sizeof (nt_resp),
- FALSE, p, &state, &save);
- p += g_base64_encode_close (FALSE, p, &state, &save);
- *p = '\0';
-
- g_free (domain_conv);
- g_free (user_conv);
- g_free (host_conv);
-
- return out;
-}
-
-/* DES utils */
-/* Set up a key schedule based on a 56bit key */
-static void
-setup_schedule (const guchar *key_56, DES_KS ks)
-{
- guchar key[8];
- int i, c, bit;
-
- key[0] = (key_56[0]) ;
- key[1] = (key_56[1] >> 1) | ((key_56[0] << 7) & 0xFF);
- key[2] = (key_56[2] >> 2) | ((key_56[1] << 6) & 0xFF);
- key[3] = (key_56[3] >> 3) | ((key_56[2] << 5) & 0xFF);
- key[4] = (key_56[4] >> 4) | ((key_56[3] << 4) & 0xFF);
- key[5] = (key_56[5] >> 5) | ((key_56[4] << 3) & 0xFF);
- key[6] = (key_56[6] >> 6) | ((key_56[5] << 2) & 0xFF);
- key[7] = ((key_56[6] << 1) & 0xFF);
-
- /* Fix parity */
- for (i = 0; i < 8; i++) {
- for (c = bit = 0; bit < 8; bit++)
- if (key[i] & (1 << bit))
- c++;
- if (!(c & 1))
- key[i] ^= 0x01;
- }
-
- deskey (ks, key, 0);
-}
-
-static void
-calc_response (const guchar *key, const guchar *plaintext, guchar *results)
-{
- DES_KS ks;
-
- memcpy (results, plaintext, 8);
- memcpy (results + 8, plaintext, 8);
- memcpy (results + 16, plaintext, 8);
-
- setup_schedule (key, ks);
- des (ks, results);
-
- setup_schedule (key + 7, ks);
- des (ks, results + 8);
-
- setup_schedule (key + 14, ks);
- des (ks, results + 16);
-}
-
-
-/*
- * MD4 encoder. (The one everyone else uses is not GPL-compatible;
- * this is a reimplementation from spec.) This doesn't need to be
- * efficient for our purposes, although it would be nice to fix
- * it to not malloc()...
- */
-
-#define F(X,Y,Z) ( ((X)&(Y)) | ((~(X))&(Z)) )
-#define G(X,Y,Z) ( ((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)) )
-#define H(X,Y,Z) ( (X)^(Y)^(Z) )
-#define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) )
-
-static void
-md4sum (const unsigned char *in, int nbytes, unsigned char digest[16])
-{
- unsigned char *M;
- guint32 A, B, C, D, AA, BB, CC, DD, X[16];
- int pbytes, nbits = nbytes * 8, i, j;
-
- pbytes = (120 - (nbytes % 64)) % 64;
- M = alloca (nbytes + pbytes + 8);
- memcpy (M, in, nbytes);
- memset (M + nbytes, 0, pbytes + 8);
- M[nbytes] = 0x80;
- M[nbytes + pbytes] = nbits & 0xFF;
- M[nbytes + pbytes + 1] = (nbits >> 8) & 0xFF;
- M[nbytes + pbytes + 2] = (nbits >> 16) & 0xFF;
- M[nbytes + pbytes + 3] = (nbits >> 24) & 0xFF;
-
- A = 0x67452301;
- B = 0xEFCDAB89;
- C = 0x98BADCFE;
- D = 0x10325476;
-
- for (i = 0; i < nbytes + pbytes + 8; i += 64) {
- for (j = 0; j < 16; j++) {
- X[j] = (M[i + j*4]) |
- (M[i + j*4 + 1] << 8) |
- (M[i + j*4 + 2] << 16) |
- (M[i + j*4 + 3] << 24);
- }
-
- AA = A;
- BB = B;
- CC = C;
- DD = D;
-
- A = ROT (A + F(B, C, D) + X[0], 3);
- D = ROT (D + F(A, B, C) + X[1], 7);
- C = ROT (C + F(D, A, B) + X[2], 11);
- B = ROT (B + F(C, D, A) + X[3], 19);
- A = ROT (A + F(B, C, D) + X[4], 3);
- D = ROT (D + F(A, B, C) + X[5], 7);
- C = ROT (C + F(D, A, B) + X[6], 11);
- B = ROT (B + F(C, D, A) + X[7], 19);
- A = ROT (A + F(B, C, D) + X[8], 3);
- D = ROT (D + F(A, B, C) + X[9], 7);
- C = ROT (C + F(D, A, B) + X[10], 11);
- B = ROT (B + F(C, D, A) + X[11], 19);
- A = ROT (A + F(B, C, D) + X[12], 3);
- D = ROT (D + F(A, B, C) + X[13], 7);
- C = ROT (C + F(D, A, B) + X[14], 11);
- B = ROT (B + F(C, D, A) + X[15], 19);
-
- A = ROT (A + G(B, C, D) + X[0] + 0x5A827999, 3);
- D = ROT (D + G(A, B, C) + X[4] + 0x5A827999, 5);
- C = ROT (C + G(D, A, B) + X[8] + 0x5A827999, 9);
- B = ROT (B + G(C, D, A) + X[12] + 0x5A827999, 13);
- A = ROT (A + G(B, C, D) + X[1] + 0x5A827999, 3);
- D = ROT (D + G(A, B, C) + X[5] + 0x5A827999, 5);
- C = ROT (C + G(D, A, B) + X[9] + 0x5A827999, 9);
- B = ROT (B + G(C, D, A) + X[13] + 0x5A827999, 13);
- A = ROT (A + G(B, C, D) + X[2] + 0x5A827999, 3);
- D = ROT (D + G(A, B, C) + X[6] + 0x5A827999, 5);
- C = ROT (C + G(D, A, B) + X[10] + 0x5A827999, 9);
- B = ROT (B + G(C, D, A) + X[14] + 0x5A827999, 13);
- A = ROT (A + G(B, C, D) + X[3] + 0x5A827999, 3);
- D = ROT (D + G(A, B, C) + X[7] + 0x5A827999, 5);
- C = ROT (C + G(D, A, B) + X[11] + 0x5A827999, 9);
- B = ROT (B + G(C, D, A) + X[15] + 0x5A827999, 13);
-
- A = ROT (A + H(B, C, D) + X[0] + 0x6ED9EBA1, 3);
- D = ROT (D + H(A, B, C) + X[8] + 0x6ED9EBA1, 9);
- C = ROT (C + H(D, A, B) + X[4] + 0x6ED9EBA1, 11);
- B = ROT (B + H(C, D, A) + X[12] + 0x6ED9EBA1, 15);
- A = ROT (A + H(B, C, D) + X[2] + 0x6ED9EBA1, 3);
- D = ROT (D + H(A, B, C) + X[10] + 0x6ED9EBA1, 9);
- C = ROT (C + H(D, A, B) + X[6] + 0x6ED9EBA1, 11);
- B = ROT (B + H(C, D, A) + X[14] + 0x6ED9EBA1, 15);
- A = ROT (A + H(B, C, D) + X[1] + 0x6ED9EBA1, 3);
- D = ROT (D + H(A, B, C) + X[9] + 0x6ED9EBA1, 9);
- C = ROT (C + H(D, A, B) + X[5] + 0x6ED9EBA1, 11);
- B = ROT (B + H(C, D, A) + X[13] + 0x6ED9EBA1, 15);
- A = ROT (A + H(B, C, D) + X[3] + 0x6ED9EBA1, 3);
- D = ROT (D + H(A, B, C) + X[11] + 0x6ED9EBA1, 9);
- C = ROT (C + H(D, A, B) + X[7] + 0x6ED9EBA1, 11);
- B = ROT (B + H(C, D, A) + X[15] + 0x6ED9EBA1, 15);
-
- A += AA;
- B += BB;
- C += CC;
- D += DD;
- }
-
- digest[0] = A & 0xFF;
- digest[1] = (A >> 8) & 0xFF;
- digest[2] = (A >> 16) & 0xFF;
- digest[3] = (A >> 24) & 0xFF;
- digest[4] = B & 0xFF;
- digest[5] = (B >> 8) & 0xFF;
- digest[6] = (B >> 16) & 0xFF;
- digest[7] = (B >> 24) & 0xFF;
- digest[8] = C & 0xFF;
- digest[9] = (C >> 8) & 0xFF;
- digest[10] = (C >> 16) & 0xFF;
- digest[11] = (C >> 24) & 0xFF;
- digest[12] = D & 0xFF;
- digest[13] = (D >> 8) & 0xFF;
- digest[14] = (D >> 16) & 0xFF;
- digest[15] = (D >> 24) & 0xFF;
-}
-
-
-/* Public domain DES implementation from Phil Karn */
-static const guint32 Spbox[8][64] = {
- { 0x01010400,0x00000000,0x00010000,0x01010404,
- 0x01010004,0x00010404,0x00000004,0x00010000,
- 0x00000400,0x01010400,0x01010404,0x00000400,
- 0x01000404,0x01010004,0x01000000,0x00000004,
- 0x00000404,0x01000400,0x01000400,0x00010400,
- 0x00010400,0x01010000,0x01010000,0x01000404,
- 0x00010004,0x01000004,0x01000004,0x00010004,
- 0x00000000,0x00000404,0x00010404,0x01000000,
- 0x00010000,0x01010404,0x00000004,0x01010000,
- 0x01010400,0x01000000,0x01000000,0x00000400,
- 0x01010004,0x00010000,0x00010400,0x01000004,
- 0x00000400,0x00000004,0x01000404,0x00010404,
- 0x01010404,0x00010004,0x01010000,0x01000404,
- 0x01000004,0x00000404,0x00010404,0x01010400,
- 0x00000404,0x01000400,0x01000400,0x00000000,
- 0x00010004,0x00010400,0x00000000,0x01010004 },
- { 0x80108020,0x80008000,0x00008000,0x00108020,
- 0x00100000,0x00000020,0x80100020,0x80008020,
- 0x80000020,0x80108020,0x80108000,0x80000000,
- 0x80008000,0x00100000,0x00000020,0x80100020,
- 0x00108000,0x00100020,0x80008020,0x00000000,
- 0x80000000,0x00008000,0x00108020,0x80100000,
- 0x00100020,0x80000020,0x00000000,0x00108000,
- 0x00008020,0x80108000,0x80100000,0x00008020,
- 0x00000000,0x00108020,0x80100020,0x00100000,
- 0x80008020,0x80100000,0x80108000,0x00008000,
- 0x80100000,0x80008000,0x00000020,0x80108020,
- 0x00108020,0x00000020,0x00008000,0x80000000,
- 0x00008020,0x80108000,0x00100000,0x80000020,
- 0x00100020,0x80008020,0x80000020,0x00100020,
- 0x00108000,0x00000000,0x80008000,0x00008020,
- 0x80000000,0x80100020,0x80108020,0x00108000 },
- { 0x00000208,0x08020200,0x00000000,0x08020008,
- 0x08000200,0x00000000,0x00020208,0x08000200,
- 0x00020008,0x08000008,0x08000008,0x00020000,
- 0x08020208,0x00020008,0x08020000,0x00000208,
- 0x08000000,0x00000008,0x08020200,0x00000200,
- 0x00020200,0x08020000,0x08020008,0x00020208,
- 0x08000208,0x00020200,0x00020000,0x08000208,
- 0x00000008,0x08020208,0x00000200,0x08000000,
- 0x08020200,0x08000000,0x00020008,0x00000208,
- 0x00020000,0x08020200,0x08000200,0x00000000,
- 0x00000200,0x00020008,0x08020208,0x08000200,
- 0x08000008,0x00000200,0x00000000,0x08020008,
- 0x08000208,0x00020000,0x08000000,0x08020208,
- 0x00000008,0x00020208,0x00020200,0x08000008,
- 0x08020000,0x08000208,0x00000208,0x08020000,
- 0x00020208,0x00000008,0x08020008,0x00020200 },
- { 0x00802001,0x00002081,0x00002081,0x00000080,
- 0x00802080,0x00800081,0x00800001,0x00002001,
- 0x00000000,0x00802000,0x00802000,0x00802081,
- 0x00000081,0x00000000,0x00800080,0x00800001,
- 0x00000001,0x00002000,0x00800000,0x00802001,
- 0x00000080,0x00800000,0x00002001,0x00002080,
- 0x00800081,0x00000001,0x00002080,0x00800080,
- 0x00002000,0x00802080,0x00802081,0x00000081,
- 0x00800080,0x00800001,0x00802000,0x00802081,
- 0x00000081,0x00000000,0x00000000,0x00802000,
- 0x00002080,0x00800080,0x00800081,0x00000001,
- 0x00802001,0x00002081,0x00002081,0x00000080,
- 0x00802081,0x00000081,0x00000001,0x00002000,
- 0x00800001,0x00002001,0x00802080,0x00800081,
- 0x00002001,0x00002080,0x00800000,0x00802001,
- 0x00000080,0x00800000,0x00002000,0x00802080 },
- { 0x00000100,0x02080100,0x02080000,0x42000100,
- 0x00080000,0x00000100,0x40000000,0x02080000,
- 0x40080100,0x00080000,0x02000100,0x40080100,
- 0x42000100,0x42080000,0x00080100,0x40000000,
- 0x02000000,0x40080000,0x40080000,0x00000000,
- 0x40000100,0x42080100,0x42080100,0x02000100,
- 0x42080000,0x40000100,0x00000000,0x42000000,
- 0x02080100,0x02000000,0x42000000,0x00080100,
- 0x00080000,0x42000100,0x00000100,0x02000000,
- 0x40000000,0x02080000,0x42000100,0x40080100,
- 0x02000100,0x40000000,0x42080000,0x02080100,
- 0x40080100,0x00000100,0x02000000,0x42080000,
- 0x42080100,0x00080100,0x42000000,0x42080100,
- 0x02080000,0x00000000,0x40080000,0x42000000,
- 0x00080100,0x02000100,0x40000100,0x00080000,
- 0x00000000,0x40080000,0x02080100,0x40000100 },
- { 0x20000010,0x20400000,0x00004000,0x20404010,
- 0x20400000,0x00000010,0x20404010,0x00400000,
- 0x20004000,0x00404010,0x00400000,0x20000010,
- 0x00400010,0x20004000,0x20000000,0x00004010,
- 0x00000000,0x00400010,0x20004010,0x00004000,
- 0x00404000,0x20004010,0x00000010,0x20400010,
- 0x20400010,0x00000000,0x00404010,0x20404000,
- 0x00004010,0x00404000,0x20404000,0x20000000,
- 0x20004000,0x00000010,0x20400010,0x00404000,
- 0x20404010,0x00400000,0x00004010,0x20000010,
- 0x00400000,0x20004000,0x20000000,0x00004010,
- 0x20000010,0x20404010,0x00404000,0x20400000,
- 0x00404010,0x20404000,0x00000000,0x20400010,
- 0x00000010,0x00004000,0x20400000,0x00404010,
- 0x00004000,0x00400010,0x20004010,0x00000000,
- 0x20404000,0x20000000,0x00400010,0x20004010 },
- { 0x00200000,0x04200002,0x04000802,0x00000000,
- 0x00000800,0x04000802,0x00200802,0x04200800,
- 0x04200802,0x00200000,0x00000000,0x04000002,
- 0x00000002,0x04000000,0x04200002,0x00000802,
- 0x04000800,0x00200802,0x00200002,0x04000800,
- 0x04000002,0x04200000,0x04200800,0x00200002,
- 0x04200000,0x00000800,0x00000802,0x04200802,
- 0x00200800,0x00000002,0x04000000,0x00200800,
- 0x04000000,0x00200800,0x00200000,0x04000802,
- 0x04000802,0x04200002,0x04200002,0x00000002,
- 0x00200002,0x04000000,0x04000800,0x00200000,
- 0x04200800,0x00000802,0x00200802,0x04200800,
- 0x00000802,0x04000002,0x04200802,0x04200000,
- 0x00200800,0x00000000,0x00000002,0x04200802,
- 0x00000000,0x00200802,0x04200000,0x00000800,
- 0x04000002,0x04000800,0x00000800,0x00200002 },
- { 0x10001040,0x00001000,0x00040000,0x10041040,
- 0x10000000,0x10001040,0x00000040,0x10000000,
- 0x00040040,0x10040000,0x10041040,0x00041000,
- 0x10041000,0x00041040,0x00001000,0x00000040,
- 0x10040000,0x10000040,0x10001000,0x00001040,
- 0x00041000,0x00040040,0x10040040,0x10041000,
- 0x00001040,0x00000000,0x00000000,0x10040040,
- 0x10000040,0x10001000,0x00041040,0x00040000,
- 0x00041040,0x00040000,0x10041000,0x00001000,
- 0x00000040,0x10040040,0x00001000,0x00041040,
- 0x10001000,0x00000040,0x10000040,0x10040000,
- 0x10040040,0x10000000,0x00040000,0x10001040,
- 0x00000000,0x10041040,0x00040040,0x10000040,
- 0x10040000,0x10001000,0x10001040,0x00000000,
- 0x10041040,0x00041000,0x00041000,0x00001040,
- 0x00001040,0x00040040,0x10000000,0x10041000 }
-};
-
-#undef F
-#define F(l,r,key){\
- work = ((r >> 4) | (r << 28)) ^ key[0];\
- l ^= Spbox[6][work & 0x3f];\
- l ^= Spbox[4][(work >> 8) & 0x3f];\
- l ^= Spbox[2][(work >> 16) & 0x3f];\
- l ^= Spbox[0][(work >> 24) & 0x3f];\
- work = r ^ key[1];\
- l ^= Spbox[7][work & 0x3f];\
- l ^= Spbox[5][(work >> 8) & 0x3f];\
- l ^= Spbox[3][(work >> 16) & 0x3f];\
- l ^= Spbox[1][(work >> 24) & 0x3f];\
-}
-/* Encrypt or decrypt a block of data in ECB mode */
-static void
-des (guint32 ks[16][2], unsigned char block[8])
-{
- guint32 left,right,work;
-
- /* Read input block and place in left/right in big-endian order */
- left = ((guint32)block[0] << 24)
- | ((guint32)block[1] << 16)
- | ((guint32)block[2] << 8)
- | (guint32)block[3];
- right = ((guint32)block[4] << 24)
- | ((guint32)block[5] << 16)
- | ((guint32)block[6] << 8)
- | (guint32)block[7];
-
- /* Hoey's clever initial permutation algorithm, from Outerbridge
- * (see Schneier p 478)
- *
- * The convention here is the same as Outerbridge: rotate each
- * register left by 1 bit, i.e., so that "left" contains permuted
- * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32
- * (using origin-1 numbering as in the FIPS). This allows us to avoid
- * one of the two rotates that would otherwise be required in each of
- * the 16 rounds.
- */
- work = ((left >> 4) ^ right) & 0x0f0f0f0f;
- right ^= work;
- left ^= work << 4;
- work = ((left >> 16) ^ right) & 0xffff;
- right ^= work;
- left ^= work << 16;
- work = ((right >> 2) ^ left) & 0x33333333;
- left ^= work;
- right ^= (work << 2);
- work = ((right >> 8) ^ left) & 0xff00ff;
- left ^= work;
- right ^= (work << 8);
- right = (right << 1) | (right >> 31);
- work = (left ^ right) & 0xaaaaaaaa;
- left ^= work;
- right ^= work;
- left = (left << 1) | (left >> 31);
-
- /* Now do the 16 rounds */
- F(left,right,ks[0]);
- F(right,left,ks[1]);
- F(left,right,ks[2]);
- F(right,left,ks[3]);
- F(left,right,ks[4]);
- F(right,left,ks[5]);
- F(left,right,ks[6]);
- F(right,left,ks[7]);
- F(left,right,ks[8]);
- F(right,left,ks[9]);
- F(left,right,ks[10]);
- F(right,left,ks[11]);
- F(left,right,ks[12]);
- F(right,left,ks[13]);
- F(left,right,ks[14]);
- F(right,left,ks[15]);
-
- /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
- right = (right << 31) | (right >> 1);
- work = (left ^ right) & 0xaaaaaaaa;
- left ^= work;
- right ^= work;
- left = (left >> 1) | (left << 31);
- work = ((left >> 8) ^ right) & 0xff00ff;
- right ^= work;
- left ^= work << 8;
- work = ((left >> 2) ^ right) & 0x33333333;
- right ^= work;
- left ^= work << 2;
- work = ((right >> 16) ^ left) & 0xffff;
- left ^= work;
- right ^= work << 16;
- work = ((right >> 4) ^ left) & 0x0f0f0f0f;
- left ^= work;
- right ^= work << 4;
-
- /* Put the block back into the user's buffer with final swap */
- block[0] = right >> 24;
- block[1] = right >> 16;
- block[2] = right >> 8;
- block[3] = right;
- block[4] = left >> 24;
- block[5] = left >> 16;
- block[6] = left >> 8;
- block[7] = left;
-}
-
-/* Key schedule-related tables from FIPS-46 */
-
-/* permuted choice table (key) */
-static const unsigned char pc1[] = {
- 57, 49, 41, 33, 25, 17, 9,
- 1, 58, 50, 42, 34, 26, 18,
- 10, 2, 59, 51, 43, 35, 27,
- 19, 11, 3, 60, 52, 44, 36,
-
- 63, 55, 47, 39, 31, 23, 15,
- 7, 62, 54, 46, 38, 30, 22,
- 14, 6, 61, 53, 45, 37, 29,
- 21, 13, 5, 28, 20, 12, 4
-};
-
-/* number left rotations of pc1 */
-static const unsigned char totrot[] = {
- 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
-};
-
-/* permuted choice key (table) */
-static const unsigned char pc2[] = {
- 14, 17, 11, 24, 1, 5,
- 3, 28, 15, 6, 21, 10,
- 23, 19, 12, 4, 26, 8,
- 16, 7, 27, 20, 13, 2,
- 41, 52, 31, 37, 47, 55,
- 30, 40, 51, 45, 33, 48,
- 44, 49, 39, 56, 34, 53,
- 46, 42, 50, 36, 29, 32
-};
-
-/* End of DES-defined tables */
-
-
-/* bit 0 is left-most in byte */
-static const int bytebit[] = {
- 0200,0100,040,020,010,04,02,01
-};
-
-
-/* Generate key schedule for encryption or decryption
- * depending on the value of "decrypt"
- */
-static void
-deskey (DES_KS k, unsigned char *key, int decrypt)
-{
- unsigned char pc1m[56]; /* place to modify pc1 into */
- unsigned char pcr[56]; /* place to rotate pc1 into */
- register int i,j,l;
- int m;
- unsigned char ks[8];
-
- for (j=0; j<56; j++) { /* convert pc1 to bits of key */
- l=pc1[j]-1; /* integer bit location */
- m = l & 07; /* find bit */
- pc1m[j]=(key[l>>3] & /* find which key byte l is in */
- bytebit[m]) /* and which bit of that byte */
- ? 1 : 0; /* and store 1-bit result */
- }
- for (i=0; i<16; i++) { /* key chunk for each iteration */
- memset(ks,0,sizeof(ks)); /* Clear key schedule */
- for (j=0; j<56; j++) /* rotate pc1 the right amount */
- pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
- /* rotate left and right halves independently */
- for (j=0; j<48; j++){ /* select bits individually */
- /* check bit that goes to ks[j] */
- if (pcr[pc2[j]-1]){
- /* mask it in if it's there */
- l= j % 6;
- ks[j/6] |= bytebit[l] >> 2;
- }
- }
- /* Now convert to packed odd/even interleaved form */
- k[i][0] = ((guint32)ks[0] << 24)
- | ((guint32)ks[2] << 16)
- | ((guint32)ks[4] << 8)
- | ((guint32)ks[6]);
- k[i][1] = ((guint32)ks[1] << 24)
- | ((guint32)ks[3] << 16)
- | ((guint32)ks[5] << 8)
- | ((guint32)ks[7]);
- }
-}
diff --git a/libsoup/soup-auth-manager-ntlm.h b/libsoup/soup-auth-manager-ntlm.h
deleted file mode 100644
index f0b4f575..00000000
--- a/libsoup/soup-auth-manager-ntlm.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2008 Red Hat, Inc.
- */
-
-#ifndef SOUP_AUTH_MANAGER_NTLM_H
-#define SOUP_AUTH_MANAGER_NTLM_H 1
-
-#include "soup-auth-manager.h"
-
-G_BEGIN_DECLS
-
-#define SOUP_TYPE_AUTH_MANAGER_NTLM (soup_auth_manager_ntlm_get_type ())
-#define SOUP_AUTH_MANAGER_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLM))
-#define SOUP_AUTH_MANAGER_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
-#define SOUP_IS_AUTH_MANAGER_NTLM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_MANAGER_NTLM))
-#define SOUP_IS_AUTH_MANAGER_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_MANAGER_NTLM))
-#define SOUP_AUTH_MANAGER_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMClass))
-
-typedef struct {
- SoupAuthManager parent;
-
-} SoupAuthManagerNTLM;
-
-typedef struct {
- SoupAuthManagerClass parent_class;
-
-} SoupAuthManagerNTLMClass;
-
-#define SOUP_AUTH_MANAGER_NTLM_USE_NTLM "use-ntlm"
-
-GType soup_auth_manager_ntlm_get_type (void);
-
-G_END_DECLS
-
-#endif /* SOUP_AUTH_MANAGER_NTLM_NTLM_H */
diff --git a/libsoup/soup-auth-manager.c b/libsoup/soup-auth-manager.c
index b3ef9f65..f40a9282 100644
--- a/libsoup/soup-auth-manager.c
+++ b/libsoup/soup-auth-manager.c
@@ -13,12 +13,43 @@
#include "soup-auth-manager.h"
#include "soup.h"
-#include "soup-marshal.h"
+#include "soup-connection-auth.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
#include "soup-path-map.h"
#include "soup-session-private.h"
+/**
+ * SECTION:soup-auth-manager
+ * @short_description: HTTP client-side authentication handler
+ * @see_also: #SoupSession, #SoupAuth
+ *
+ * #SoupAuthManager is the #SoupSessionFeature that handles HTTP
+ * authentication for a #SoupSession.
+ *
+ * A #SoupAuthManager is added to the session by default, and normally
+ * you don't need to worry about it at all. However, if you want to
+ * disable HTTP authentication, you can remove the feature from the
+ * session with soup_session_remove_feature_by_type(), or disable it on
+ * individual requests with soup_message_disable_feature().
+ *
+ * Since: 2.42
+ **/
+
+/**
+ * SOUP_TYPE_AUTH_MANAGER:
+ *
+ * The #GType of #SoupAuthManager; you can use this with
+ * soup_session_remove_feature_by_type() or
+ * soup_message_disable_feature().
+ *
+ * (Although this type has only been publicly visible since libsoup
+ * 2.42, it has always existed in the background, and you can use
+ * <literal><code>g_type_from_name ("SoupAuthManager")</code></literal>
+ * to get its #GType in earlier releases.)
+ *
+ * Since: 2.42
+ */
static void soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
static SoupSessionFeatureInterface *soup_session_feature_default_interface;
@@ -33,14 +64,15 @@ G_DEFINE_TYPE_WITH_CODE (SoupAuthManager, soup_auth_manager, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
soup_auth_manager_session_feature_init))
-typedef struct {
+struct SoupAuthManagerPrivate {
SoupSession *session;
GPtrArray *auth_types;
+ gboolean auto_ntlm;
+ GMutex lock;
SoupAuth *proxy_auth;
GHashTable *auth_hosts;
-} SoupAuthManagerPrivate;
-#define SOUP_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerPrivate))
+};
typedef struct {
SoupURI *uri;
@@ -49,23 +81,29 @@ typedef struct {
} SoupAuthHost;
static void soup_auth_host_free (SoupAuthHost *host);
+static SoupAuth *record_auth_for_uri (SoupAuthManagerPrivate *priv,
+ SoupURI *uri, SoupAuth *auth,
+ gboolean prior_auth_failed);
static void
soup_auth_manager_init (SoupAuthManager *manager)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuthManagerPrivate *priv;
+
+ priv = manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerPrivate);
priv->auth_types = g_ptr_array_new_with_free_func ((GDestroyNotify)g_type_class_unref);
priv->auth_hosts = g_hash_table_new_full (soup_uri_host_hash,
soup_uri_host_equal,
NULL,
(GDestroyNotify)soup_auth_host_free);
+ g_mutex_init (&priv->lock);
}
static void
soup_auth_manager_finalize (GObject *object)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (object);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (object)->priv;
g_ptr_array_free (priv->auth_types, TRUE);
@@ -73,6 +111,8 @@ soup_auth_manager_finalize (GObject *object)
g_clear_object (&priv->proxy_auth);
+ g_mutex_clear (&priv->lock);
+
G_OBJECT_CLASS (soup_auth_manager_parent_class)->finalize (object);
}
@@ -85,13 +125,27 @@ soup_auth_manager_class_init (SoupAuthManagerClass *auth_manager_class)
object_class->finalize = soup_auth_manager_finalize;
+ /**
+ * SoupAuthManager::authenticate:
+ * @manager: the #SoupAuthManager
+ * @msg: the #SoupMessage being sent
+ * @auth: the #SoupAuth to authenticate
+ * @retrying: %TRUE if this is the second (or later) attempt
+ *
+ * Emitted when the manager requires the application to
+ * provide authentication credentials.
+ *
+ * #SoupSession connects to this signal and emits its own
+ * #SoupSession::authenticate signal when it is emitted, so
+ * you shouldn't need to use this signal directly.
+ */
signals[AUTHENTICATE] =
g_signal_new ("authenticate",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupAuthManagerClass, authenticate),
NULL, NULL,
- _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
+ NULL,
G_TYPE_NONE, 3,
SOUP_TYPE_MESSAGE,
SOUP_TYPE_AUTH,
@@ -111,7 +165,7 @@ auth_type_compare_func (gconstpointer a, gconstpointer b)
static gboolean
soup_auth_manager_add_feature (SoupSessionFeature *feature, GType type)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (feature)->priv;
SoupAuthClass *auth_class;
if (!g_type_is_a (type, SOUP_TYPE_AUTH))
@@ -120,13 +174,21 @@ soup_auth_manager_add_feature (SoupSessionFeature *feature, GType type)
auth_class = g_type_class_ref (type);
g_ptr_array_add (priv->auth_types, auth_class);
g_ptr_array_sort (priv->auth_types, auth_type_compare_func);
+
+ /* Plain SoupSession does not get the backward-compat
+ * auto-NTLM behavior; SoupSession subclasses do.
+ */
+ if (type == SOUP_TYPE_AUTH_NTLM &&
+ G_TYPE_FROM_INSTANCE (priv->session) != SOUP_TYPE_SESSION)
+ priv->auto_ntlm = TRUE;
+
return TRUE;
}
static gboolean
soup_auth_manager_remove_feature (SoupSessionFeature *feature, GType type)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (feature)->priv;
SoupAuthClass *auth_class;
int i;
@@ -134,8 +196,12 @@ soup_auth_manager_remove_feature (SoupSessionFeature *feature, GType type)
return FALSE;
auth_class = g_type_class_peek (type);
+
for (i = 0; i < priv->auth_types->len; i++) {
if (priv->auth_types->pdata[i] == (gpointer)auth_class) {
+ if (type == SOUP_TYPE_AUTH_NTLM)
+ priv->auto_ntlm = FALSE;
+
g_ptr_array_remove_index (priv->auth_types, i);
return TRUE;
}
@@ -147,7 +213,7 @@ soup_auth_manager_remove_feature (SoupSessionFeature *feature, GType type)
static gboolean
soup_auth_manager_has_feature (SoupSessionFeature *feature, GType type)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (feature);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (feature)->priv;
SoupAuthClass *auth_class;
int i;
@@ -162,22 +228,15 @@ soup_auth_manager_has_feature (SoupSessionFeature *feature, GType type)
return FALSE;
}
-void
-soup_auth_manager_emit_authenticate (SoupAuthManager *manager, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying)
-{
- g_signal_emit (manager, signals[AUTHENTICATE], 0, msg, auth, retrying);
-}
-
static void
-soup_auth_manager_attach (SoupSessionFeature *manager, SoupSession *session)
+soup_auth_manager_attach (SoupSessionFeature *feature, SoupSession *session)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (feature)->priv;
/* FIXME: should support multiple sessions */
priv->session = session;
- soup_session_feature_default_interface->attach (manager, session);
+ soup_session_feature_default_interface->attach (feature, session);
}
static inline const char *
@@ -252,7 +311,7 @@ next_challenge_start (GSList *items)
return NULL;
}
-char *
+static char *
soup_auth_manager_extract_challenge (const char *challenges, const char *scheme)
{
GSList *items, *i, *next;
@@ -319,28 +378,30 @@ create_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
static gboolean
check_auth (SoupMessage *msg, SoupAuth *auth)
{
- const char *header;
- char *challenge;
- gboolean ok;
+ const char *header, *scheme;
+ char *challenge = NULL;
+ gboolean ok = TRUE;
- header = auth_header_for_message (msg);
- if (!header)
- return FALSE;
+ scheme = soup_auth_get_scheme_name (auth);
- challenge = soup_auth_manager_extract_challenge (header, soup_auth_get_scheme_name (auth));
- if (!challenge)
- return FALSE;
+ header = auth_header_for_message (msg);
+ if (header)
+ challenge = soup_auth_manager_extract_challenge (header, scheme);
+ if (!challenge) {
+ ok = FALSE;
+ challenge = g_strdup (scheme);
+ }
- ok = soup_auth_update (auth, msg, challenge);
+ if (!soup_auth_update (auth, msg, challenge))
+ ok = FALSE;
g_free (challenge);
return ok;
}
static SoupAuthHost *
-get_auth_host_for_message (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+get_auth_host_for_uri (SoupAuthManagerPrivate *priv, SoupURI *uri)
{
SoupAuthHost *host;
- SoupURI *uri = soup_message_get_uri (msg);
host = g_hash_table_lookup (priv->auth_hosts, uri);
if (host)
@@ -363,14 +424,30 @@ soup_auth_host_free (SoupAuthHost *host)
g_slice_free (SoupAuthHost, host);
}
+static gboolean
+make_auto_ntlm_auth (SoupAuthManagerPrivate *priv, SoupAuthHost *host)
+{
+ SoupAuth *auth;
+
+ if (!priv->auto_ntlm)
+ return FALSE;
+
+ auth = g_object_new (SOUP_TYPE_AUTH_NTLM,
+ SOUP_AUTH_HOST, host->uri->host,
+ NULL);
+ record_auth_for_uri (priv, host->uri, auth, FALSE);
+ g_object_unref (auth);
+ return TRUE;
+}
+
static SoupAuth *
lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
{
SoupAuthHost *host;
const char *path, *realm;
- host = get_auth_host_for_message (priv, msg);
- if (!host->auth_realms)
+ host = get_auth_host_for_uri (priv, soup_message_get_uri (msg));
+ if (!host->auth_realms && !make_auto_ntlm_auth (priv, host))
return NULL;
path = soup_message_get_uri (msg)->path;
@@ -383,12 +460,12 @@ lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
return NULL;
}
-static gboolean
+static void
authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
SoupMessage *msg, gboolean prior_auth_failed,
gboolean proxy, gboolean can_interact)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuthManagerPrivate *priv = manager->priv;
SoupURI *uri;
if (proxy) {
@@ -404,48 +481,34 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
uri = NULL;
if (!uri)
- return FALSE;
+ return;
} else
uri = soup_message_get_uri (msg);
/* If a password is specified explicitly in the URI, use it
* even if the auth had previously already been authenticated.
*/
- if (uri->password) {
- if (!prior_auth_failed)
- soup_auth_authenticate (auth, uri->user, uri->password);
+ if (uri->password && uri->user) {
+ soup_auth_authenticate (auth, uri->user, uri->password);
+ soup_uri_set_password (uri, NULL);
+ soup_uri_set_user (uri, NULL);
} else if (!soup_auth_is_authenticated (auth) && can_interact) {
- soup_auth_manager_emit_authenticate (manager, msg, auth,
- prior_auth_failed);
+ g_signal_emit (manager, signals[AUTHENTICATE], 0,
+ msg, auth, prior_auth_failed);
}
-
- return soup_auth_is_authenticated (auth);
}
-static void
-update_auth (SoupMessage *msg, gpointer manager)
+static SoupAuth *
+record_auth_for_uri (SoupAuthManagerPrivate *priv, SoupURI *uri,
+ SoupAuth *auth, gboolean prior_auth_failed)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
SoupAuthHost *host;
- SoupAuth *auth, *prior_auth, *old_auth;
+ SoupAuth *old_auth;
const char *path;
char *auth_info, *old_auth_info;
GSList *pspace, *p;
- gboolean prior_auth_failed = FALSE;
-
- host = get_auth_host_for_message (priv, msg);
- /* See if we used auth last time */
- prior_auth = soup_message_get_auth (msg);
- if (prior_auth && check_auth (msg, prior_auth)) {
- auth = prior_auth;
- if (!soup_auth_is_authenticated (auth))
- prior_auth_failed = TRUE;
- } else {
- auth = create_auth (priv, msg);
- if (!auth)
- return;
- }
+ host = get_auth_host_for_uri (priv, uri);
auth_info = soup_auth_get_info (auth);
if (!host->auth_realms) {
@@ -455,7 +518,7 @@ update_auth (SoupMessage *msg, gpointer manager)
}
/* Record where this auth realm is used. */
- pspace = soup_auth_get_protection_space (auth, soup_message_get_uri (msg));
+ pspace = soup_auth_get_protection_space (auth, uri);
for (p = pspace; p; p = p->next) {
path = p->data;
old_auth_info = soup_path_map_lookup (host->auth_realms, path);
@@ -471,68 +534,116 @@ update_auth (SoupMessage *msg, gpointer manager)
soup_auth_free_protection_space (auth, pspace);
/* Now, make sure the auth is recorded. (If there's a
- * pre-existing auth, we keep that rather than the new one,
+ * pre-existing good auth, we keep that rather than the new one,
* since the old one might already be authenticated.)
*/
old_auth = g_hash_table_lookup (host->auths, auth_info);
- if (old_auth) {
+ if (old_auth && (old_auth != auth || !prior_auth_failed)) {
g_free (auth_info);
- if (auth != old_auth && auth != prior_auth) {
- g_object_unref (auth);
- auth = old_auth;
- }
+ return old_auth;
+ } else {
+ g_hash_table_insert (host->auths, auth_info,
+ g_object_ref (auth));
+ return auth;
+ }
+}
+
+static void
+auth_got_headers (SoupMessage *msg, gpointer manager)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
+ SoupAuth *auth, *prior_auth, *new_auth;
+ gboolean prior_auth_failed = FALSE;
+
+ g_mutex_lock (&priv->lock);
+
+ /* See if we used auth last time */
+ prior_auth = soup_message_get_auth (msg);
+ if (prior_auth && check_auth (msg, prior_auth)) {
+ auth = g_object_ref (prior_auth);
+ if (!soup_auth_is_ready (auth, msg))
+ prior_auth_failed = TRUE;
} else {
- g_hash_table_insert (host->auths, auth_info, auth);
+ auth = create_auth (priv, msg);
+ if (!auth) {
+ g_mutex_unlock (&priv->lock);
+ return;
+ }
}
+ new_auth = record_auth_for_uri (priv, soup_message_get_uri (msg),
+ auth, prior_auth_failed);
+ g_object_unref (auth);
+
/* If we need to authenticate, try to do it. */
- authenticate_auth (manager, auth, msg,
+ authenticate_auth (manager, new_auth, msg,
prior_auth_failed, FALSE, TRUE);
+ g_mutex_unlock (&priv->lock);
}
static void
-requeue_if_authenticated (SoupMessage *msg, gpointer manager)
+auth_got_body (SoupMessage *msg, gpointer manager)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
- SoupAuth *auth = lookup_auth (priv, msg);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
+ SoupAuth *auth;
+
+ g_mutex_lock (&priv->lock);
+ auth = lookup_auth (priv, msg);
+ if (auth && soup_auth_is_ready (auth, msg)) {
+ if (SOUP_IS_CONNECTION_AUTH (auth)) {
+ SoupMessageFlags flags;
+
+ flags = soup_message_get_flags (msg);
+ soup_message_set_flags (msg, flags & ~SOUP_MESSAGE_NEW_CONNECTION);
+ }
- if (auth && soup_auth_is_authenticated (auth))
soup_session_requeue_message (priv->session, msg);
+ }
+ g_mutex_unlock (&priv->lock);
}
static void
-update_proxy_auth (SoupMessage *msg, gpointer manager)
+proxy_auth_got_headers (SoupMessage *msg, gpointer manager)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
SoupAuth *prior_auth;
gboolean prior_auth_failed = FALSE;
+ g_mutex_lock (&priv->lock);
+
/* See if we used auth last time */
prior_auth = soup_message_get_proxy_auth (msg);
if (prior_auth && check_auth (msg, prior_auth)) {
- if (!soup_auth_is_authenticated (prior_auth))
+ if (!soup_auth_is_ready (prior_auth, msg))
prior_auth_failed = TRUE;
}
if (!priv->proxy_auth) {
priv->proxy_auth = create_auth (priv, msg);
- if (!priv->proxy_auth)
+ if (!priv->proxy_auth) {
+ g_mutex_unlock (&priv->lock);
return;
+ }
}
/* If we need to authenticate, try to do it. */
authenticate_auth (manager, priv->proxy_auth, msg,
prior_auth_failed, TRUE, TRUE);
+ g_mutex_unlock (&priv->lock);
}
static void
-requeue_if_proxy_authenticated (SoupMessage *msg, gpointer manager)
+proxy_auth_got_body (SoupMessage *msg, gpointer manager)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
- SoupAuth *auth = priv->proxy_auth;
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER (manager)->priv;
+ SoupAuth *auth;
+
+ g_mutex_lock (&priv->lock);
+ auth = priv->proxy_auth;
- if (auth && soup_auth_is_authenticated (auth))
+ if (auth && soup_auth_is_ready (auth, msg))
soup_session_requeue_message (priv->session, msg);
+ g_mutex_unlock (&priv->lock);
}
static void
@@ -542,17 +653,17 @@ soup_auth_manager_request_queued (SoupSessionFeature *manager,
{
soup_message_add_status_code_handler (
msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
- G_CALLBACK (update_auth), manager);
+ G_CALLBACK (auth_got_headers), manager);
soup_message_add_status_code_handler (
msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
- G_CALLBACK (requeue_if_authenticated), manager);
+ G_CALLBACK (auth_got_body), manager);
soup_message_add_status_code_handler (
msg, "got_headers", SOUP_STATUS_PROXY_UNAUTHORIZED,
- G_CALLBACK (update_proxy_auth), manager);
+ G_CALLBACK (proxy_auth_got_headers), manager);
soup_message_add_status_code_handler (
msg, "got_body", SOUP_STATUS_PROXY_UNAUTHORIZED,
- G_CALLBACK (requeue_if_proxy_authenticated), manager);
+ G_CALLBACK (proxy_auth_got_body), manager);
}
static void
@@ -562,18 +673,30 @@ soup_auth_manager_request_started (SoupSessionFeature *feature,
SoupSocket *socket)
{
SoupAuthManager *manager = SOUP_AUTH_MANAGER (feature);
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuthManagerPrivate *priv = manager->priv;
SoupAuth *auth;
- auth = lookup_auth (priv, msg);
- if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE, FALSE))
- auth = NULL;
- soup_message_set_auth (msg, auth);
+ g_mutex_lock (&priv->lock);
+
+ if (msg->method != SOUP_METHOD_CONNECT) {
+ auth = lookup_auth (priv, msg);
+ if (auth) {
+ authenticate_auth (manager, auth, msg, FALSE, FALSE, FALSE);
+ if (!soup_auth_is_ready (auth, msg))
+ auth = NULL;
+ }
+ soup_message_set_auth (msg, auth);
+ }
auth = priv->proxy_auth;
- if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE))
- auth = NULL;
+ if (auth) {
+ authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE);
+ if (!soup_auth_is_ready (auth, msg))
+ auth = NULL;
+ }
soup_message_set_proxy_auth (msg, auth);
+
+ g_mutex_unlock (&priv->lock);
}
static void
@@ -585,6 +708,36 @@ soup_auth_manager_request_unqueued (SoupSessionFeature *manager,
0, 0, NULL, NULL, manager);
}
+/**
+ * soup_auth_manager_use_auth:
+ * @manager: a #SoupAuthManager
+ * @uri: the #SoupURI under which @auth is to be used
+ * @auth: the #SoupAuth to use
+ *
+ * Records that @auth is to be used under @uri, as though a
+ * WWW-Authenticate header had been received at that URI. This can be
+ * used to "preload" @manager's auth cache, to avoid an extra HTTP
+ * round trip in the case where you know ahead of time that a 401
+ * response will be returned.
+ *
+ * This is only useful for authentication types where the initial
+ * Authorization header does not depend on any additional information
+ * from the server. (Eg, Basic or NTLM, but not Digest.)
+ *
+ * Since: 2.42
+ */
+void
+soup_auth_manager_use_auth (SoupAuthManager *manager,
+ SoupURI *uri,
+ SoupAuth *auth)
+{
+ SoupAuthManagerPrivate *priv = manager->priv;
+
+ g_mutex_lock (&priv->lock);
+ record_auth_for_uri (priv, uri, auth, FALSE);
+ g_mutex_unlock (&priv->lock);
+}
+
static void
soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface,
gpointer interface_data)
diff --git a/libsoup/soup-auth-manager.h b/libsoup/soup-auth-manager.h
index d82fbb1d..c1fcc6e6 100644
--- a/libsoup/soup-auth-manager.h
+++ b/libsoup/soup-auth-manager.h
@@ -18,9 +18,12 @@ G_BEGIN_DECLS
#define SOUP_IS_AUTH_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_MANAGER))
#define SOUP_AUTH_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerClass))
+typedef struct SoupAuthManagerPrivate SoupAuthManagerPrivate;
+
typedef struct {
GObject parent;
+ SoupAuthManagerPrivate *priv;
} SoupAuthManager;
typedef struct {
@@ -32,13 +35,9 @@ typedef struct {
GType soup_auth_manager_get_type (void);
-void soup_auth_manager_emit_authenticate (SoupAuthManager *manager,
- SoupMessage *msg,
- SoupAuth *auth,
- gboolean retrying);
-
-char *soup_auth_manager_extract_challenge (const char *challenges,
- const char *scheme);
+void soup_auth_manager_use_auth (SoupAuthManager *manager,
+ SoupURI *uri,
+ SoupAuth *auth);
G_END_DECLS
diff --git a/libsoup/soup-auth-ntlm.c b/libsoup/soup-auth-ntlm.c
index 391c1c28..05541973 100644
--- a/libsoup/soup-auth-ntlm.c
+++ b/libsoup/soup-auth-ntlm.c
@@ -9,21 +9,114 @@
#include <config.h>
#endif
+#include <errno.h>
+#include <stdlib.h>
#include <string.h>
+#include <glib.h>
#include "soup-auth-ntlm.h"
#include "soup.h"
+#include "soup-message-private.h"
+
+static void soup_ntlm_lanmanager_hash (const char *password,
+ guchar hash[21]);
+static void soup_ntlm_nt_hash (const char *password,
+ guchar hash[21]);
+static char *soup_ntlm_request (void);
+static gboolean soup_ntlm_parse_challenge (const char *challenge,
+ char **nonce,
+ char **default_domain);
+static char *soup_ntlm_response (const char *nonce,
+ const char *user,
+ guchar nt_hash[21],
+ guchar lm_hash[21],
+ const char *host,
+ const char *domain);
+
+typedef enum {
+ SOUP_NTLM_NEW,
+ SOUP_NTLM_SSO_FAILED,
+ SOUP_NTLM_SENT_REQUEST,
+ SOUP_NTLM_RECEIVED_CHALLENGE,
+ SOUP_NTLM_SENT_RESPONSE,
+ SOUP_NTLM_FAILED
+} SoupNTLMState;
+
+typedef struct {
+ SoupNTLMState state;
+ char *nonce;
+ char *response_header;
+} SoupNTLMConnectionState;
+
+typedef enum {
+ SOUP_NTLM_PASSWORD_NONE,
+ SOUP_NTLM_PASSWORD_PROVIDED,
+ SOUP_NTLM_PASSWORD_ACCEPTED,
+ SOUP_NTLM_PASSWORD_REJECTED
+} SoupNTLMPasswordState;
typedef struct {
- char *username, *password;
+ char *username, *domain;
+ guchar nt_hash[21], lm_hash[21];
+ SoupNTLMPasswordState password_state;
+
+#ifdef USE_NTLM_AUTH
+ /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
+ * by delegating the NTLM challenge/response protocal to a helper
+ * in ntlm_auth.
+ * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
+ * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
+ * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
+ */
+ gboolean sso_available;
+ int fd_in;
+ int fd_out;
+#endif
} SoupAuthNTLMPrivate;
#define SOUP_AUTH_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMPrivate))
-G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_AUTH)
+#ifdef USE_NTLM_AUTH
+static gboolean ntlm_auth_available, ntlm_auth_debug;
+static void sso_ntlm_close (SoupAuthNTLMPrivate *priv);
+#endif
+
+/**
+ * SOUP_TYPE_AUTH_NTLM:
+ *
+ * A #GType corresponding to HTTP-based NTLM authentication.
+ * #SoupSessions do not support this type by default; if you want to
+ * enable support for it, call soup_session_add_feature_by_type(),
+ * passing %SOUP_TYPE_AUTH_NTLM.
+ *
+ * Since: 2.34
+ */
+
+G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_CONNECTION_AUTH)
static void
soup_auth_ntlm_init (SoupAuthNTLM *ntlm)
{
+#ifdef USE_NTLM_AUTH
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (ntlm);
+ const char *username = NULL, *slash;
+
+ priv->sso_available = TRUE;
+ priv->fd_in = -1;
+ priv->fd_out = -1;
+
+ username = getenv ("NTLMUSER");
+ if (!username)
+ username = g_get_user_name ();
+
+ slash = strpbrk (username, "\\/");
+ if (slash) {
+ priv->username = g_strdup (slash + 1);
+ priv->domain = g_strndup (username, slash - username);
+ } else {
+ priv->username = g_strdup (username);
+ priv->domain = NULL;
+ }
+#endif
}
static void
@@ -32,37 +125,267 @@ soup_auth_ntlm_finalize (GObject *object)
SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (object);
g_free (priv->username);
- if (priv->password) {
- memset (priv->password, 0, strlen (priv->password));
- g_free (priv->password);
- }
+ g_free (priv->domain);
+
+ memset (priv->nt_hash, 0, sizeof (priv->nt_hash));
+ memset (priv->lm_hash, 0, sizeof (priv->lm_hash));
+
+#ifdef USE_NTLM_AUTH
+ sso_ntlm_close (priv);
+#endif
G_OBJECT_CLASS (soup_auth_ntlm_parent_class)->finalize (object);
}
-SoupAuth *
-soup_auth_ntlm_new (const char *realm, const char *host)
+#ifdef USE_NTLM_AUTH
+static void
+sso_ntlm_close (SoupAuthNTLMPrivate *priv)
{
- SoupAuth *auth;
+ if (priv->fd_in != -1) {
+ close (priv->fd_in);
+ priv->fd_in = -1;
+ }
- auth = g_object_new (SOUP_TYPE_AUTH_NTLM,
- SOUP_AUTH_REALM, realm,
- SOUP_AUTH_HOST, host,
- NULL);
- return auth;
+ if (priv->fd_out != -1) {
+ close (priv->fd_out);
+ priv->fd_out = -1;
+ }
}
static gboolean
-soup_auth_ntlm_update (SoupAuth *auth, SoupMessage *msg,
- GHashTable *auth_params)
+sso_ntlm_initiate (SoupAuthNTLMPrivate *priv)
{
- g_return_val_if_reached (FALSE);
+ char *argv[9];
+ gboolean ret;
+
+ if (!priv->sso_available)
+ return FALSE;
+
+ if (!ntlm_auth_available && !ntlm_auth_debug) {
+ priv->sso_available = FALSE;
+ return FALSE;
+ }
+
+ /* Return if ntlm_auth execution process exist already */
+ if (priv->fd_in != -1 && priv->fd_out != -1)
+ return TRUE;
+ else {
+ /* Clean all sso data before re-initiate */
+ sso_ntlm_close (priv);
+ }
+
+ if (ntlm_auth_debug) {
+ argv[0] = (char *) g_getenv ("SOUP_NTLM_AUTH_DEBUG");
+ if (!*argv[0]) {
+ priv->sso_available = FALSE;
+ return FALSE;
+ }
+ } else
+ argv[0] = NTLM_AUTH;
+ argv[1] = "--helper-protocol";
+ argv[2] = "ntlmssp-client-1";
+ argv[3] = "--use-cached-creds";
+ argv[4] = "--username";
+ argv[5] = priv->username;
+ argv[6] = priv->domain ? "--domain" : NULL;
+ argv[7] = priv->domain;
+ argv[8] = NULL;
+
+ ret = g_spawn_async_with_pipes (NULL, argv, NULL,
+ G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL,
+ NULL, &priv->fd_in, &priv->fd_out,
+ NULL, NULL);
+ if (!ret)
+ priv->sso_available = FALSE;
+ return ret;
+}
+
+static char *
+sso_ntlm_response (SoupAuthNTLMPrivate *priv, const char *input, SoupNTLMState conn_state)
+{
+ ssize_t size;
+ char buf[1024];
+ char *tmpbuf = buf;
+ size_t len_in = strlen (input), len_out = sizeof (buf);
+
+ while (len_in > 0) {
+ int written = write (priv->fd_in, input, len_in);
+ if (written == -1) {
+ if (errno == EINTR)
+ continue;
+ /* write failed if other errors happen */
+ return NULL;
+ }
+ input += written;
+ len_in -= written;
+ }
+ /* Read one line */
+ while (len_out > 0) {
+ size = read (priv->fd_out, tmpbuf, len_out);
+ if (size == -1) {
+ if (errno == EINTR)
+ continue;
+ return NULL;
+ } else if (size == 0)
+ return NULL;
+ else if (tmpbuf[size - 1] == '\n') {
+ tmpbuf[size - 1] = '\0';
+ goto wrfinish;
+ }
+ tmpbuf += size;
+ len_out -= size;
+ }
+ return NULL;
+
+wrfinish:
+ if (g_ascii_strcasecmp (buf, "PW") == 0) {
+ /* Samba/winbind installed but not configured */
+ return g_strdup ("PW");
+ }
+ if (conn_state == SOUP_NTLM_NEW &&
+ g_ascii_strncasecmp (buf, "YR ", 3) != 0) {
+ /* invalid response for type 1 message */
+ return NULL;
+ }
+ if (conn_state == SOUP_NTLM_RECEIVED_CHALLENGE &&
+ g_ascii_strncasecmp (buf, "KK ", 3) != 0 &&
+ g_ascii_strncasecmp (buf, "AF ", 3) != 0) {
+ /* invalid response for type 3 message */
+ return NULL;
+ }
+
+ return g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
+}
+#endif /* USE_NTLM_AUTH */
+
+static gpointer
+soup_auth_ntlm_create_connection_state (SoupConnectionAuth *auth)
+{
+ SoupNTLMConnectionState *conn;
+
+ conn = g_slice_new0 (SoupNTLMConnectionState);
+ conn->state = SOUP_NTLM_NEW;
+
+ return conn;
+}
+
+static void
+soup_auth_ntlm_free_connection_state (SoupConnectionAuth *auth,
+ gpointer state)
+{
+ SoupNTLMConnectionState *conn = state;
+
+ g_free (conn->nonce);
+ g_free (conn->response_header);
+ g_slice_free (SoupNTLMConnectionState, conn);
+}
+
+static gboolean
+soup_auth_ntlm_update_connection (SoupConnectionAuth *auth, SoupMessage *msg,
+ const char *auth_header, gpointer state)
+{
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ SoupNTLMConnectionState *conn = state;
+ gboolean success = TRUE;
+
+ /* Note that we only return FALSE if some sort of parsing error
+ * occurs. Otherwise, the SoupAuth is still reusable (though it may
+ * no longer be _ready or _authenticated).
+ */
+
+ if (!g_str_has_prefix (auth_header, "NTLM"))
+ return FALSE;
+
+ if (conn->state > SOUP_NTLM_SENT_REQUEST) {
+ if (priv->password_state == SOUP_NTLM_PASSWORD_ACCEPTED) {
+ /* We know our password is correct, so a 401
+ * means "permission denied". Since the conn
+ * state is now FAILED, the auth is no longer
+ * is_ready() for this message, so this will
+ * cause a "retrying" authenticate signal.
+ */
+ conn->state = SOUP_NTLM_FAILED;
+ return TRUE;
+ }
+
+#ifdef USE_NTLM_AUTH
+ if (priv->sso_available) {
+ conn->state = SOUP_NTLM_SSO_FAILED;
+ priv->password_state = SOUP_NTLM_PASSWORD_NONE;
+ } else {
+#endif
+ conn->state = SOUP_NTLM_FAILED;
+ priv->password_state = SOUP_NTLM_PASSWORD_REJECTED;
+#ifdef USE_NTLM_AUTH
+ }
+#endif
+ return TRUE;
+ }
+
+ if (conn->state == SOUP_NTLM_NEW && !auth_header[4])
+ return TRUE;
+
+ if (!soup_ntlm_parse_challenge (auth_header + 5, &conn->nonce,
+ priv->domain ? NULL : &priv->domain)) {
+ conn->state = SOUP_NTLM_FAILED;
+ return FALSE;
+ }
+
+#ifdef USE_NTLM_AUTH
+ if (priv->sso_available && conn->state == SOUP_NTLM_SENT_REQUEST) {
+ char *input, *response;
+
+ /* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
+ if (!sso_ntlm_initiate (priv)) {
+ conn->state = SOUP_NTLM_SSO_FAILED;
+ success = FALSE;
+ goto out;
+ }
+
+ input = g_strdup_printf ("TT %s\n", auth_header + 5);
+ response = sso_ntlm_response (priv, input, conn->state);
+ sso_ntlm_close (priv);
+ g_free (input);
+
+ if (!response) {
+ conn->state = SOUP_NTLM_SSO_FAILED;
+ success = FALSE;
+ } else if (!g_ascii_strcasecmp (response, "PW")) {
+ priv->sso_available = FALSE;
+ g_free (response);
+ } else {
+ conn->response_header = response;
+ if (priv->password_state != SOUP_NTLM_PASSWORD_ACCEPTED)
+ priv->password_state = SOUP_NTLM_PASSWORD_PROVIDED;
+ }
+ }
+ out:
+#endif
+
+ if (conn->state == SOUP_NTLM_SENT_REQUEST)
+ conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
+
+ g_object_set (G_OBJECT (auth),
+ SOUP_AUTH_REALM, priv->domain,
+ SOUP_AUTH_HOST, soup_message_get_uri (msg)->host,
+ NULL);
+ return success;
}
static GSList *
soup_auth_ntlm_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
{
- g_return_val_if_reached (NULL);
+ char *space, *p;
+
+ space = g_strdup (source_uri->path);
+
+ /* Strip filename component */
+ p = strrchr (space, '/');
+ if (p && p != space && p[1])
+ *p = '\0';
+
+ return g_slist_prepend (NULL, space);
}
static void
@@ -70,30 +393,148 @@ soup_auth_ntlm_authenticate (SoupAuth *auth, const char *username,
const char *password)
{
SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ const char *slash;
g_return_if_fail (username != NULL);
g_return_if_fail (password != NULL);
- priv->username = g_strdup (username);
- priv->password = g_strdup (password);
+ if (priv->username)
+ g_free (priv->username);
+ if (priv->domain)
+ g_free (priv->domain);
+
+ slash = strpbrk (username, "\\/");
+ if (slash) {
+ priv->domain = g_strndup (username, slash - username);
+ priv->username = g_strdup (slash + 1);
+ } else {
+ priv->domain = g_strdup ("");
+ priv->username = g_strdup (username);
+ }
+
+ soup_ntlm_nt_hash (password, priv->nt_hash);
+ soup_ntlm_lanmanager_hash (password, priv->lm_hash);
+
+ priv->password_state = SOUP_NTLM_PASSWORD_PROVIDED;
}
static gboolean
soup_auth_ntlm_is_authenticated (SoupAuth *auth)
{
- return SOUP_AUTH_NTLM_GET_PRIVATE (auth)->password != NULL;
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+ return (priv->password_state != SOUP_NTLM_PASSWORD_NONE &&
+ priv->password_state != SOUP_NTLM_PASSWORD_REJECTED);
+}
+
+static gboolean
+soup_auth_ntlm_is_connection_ready (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer state)
+{
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ SoupNTLMConnectionState *conn = state;
+
+ if (priv->password_state == SOUP_NTLM_PASSWORD_REJECTED)
+ return FALSE;
+
+ if (priv->password_state == SOUP_NTLM_PASSWORD_PROVIDED)
+ return TRUE;
+
+ return conn->state != SOUP_NTLM_FAILED;
+}
+
+static void
+got_final_auth_result (SoupMessage *msg, gpointer data)
+{
+ SoupAuth *auth = data;
+
+ g_signal_handlers_disconnect_by_func (msg, G_CALLBACK (got_final_auth_result), auth);
+
+ if (auth != soup_message_get_auth (msg))
+ return;
+
+ if (msg->status_code != SOUP_STATUS_UNAUTHORIZED)
+ SOUP_AUTH_NTLM_GET_PRIVATE (auth)->password_state = SOUP_NTLM_PASSWORD_ACCEPTED;
}
static char *
-soup_auth_ntlm_get_authorization (SoupAuth *auth, SoupMessage *msg)
+soup_auth_ntlm_get_connection_authorization (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer state)
{
- g_return_val_if_reached (NULL);
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ SoupNTLMConnectionState *conn = state;
+ char *header = NULL;
+
+ switch (conn->state) {
+ case SOUP_NTLM_NEW:
+#ifdef USE_NTLM_AUTH
+ if (sso_ntlm_initiate (priv)) {
+ header = sso_ntlm_response (priv, "YR\n", conn->state);
+ if (header) {
+ if (g_ascii_strcasecmp (header, "PW") != 0) {
+ conn->state = SOUP_NTLM_SENT_REQUEST;
+ break;
+ } else {
+ g_free (header);
+ header = NULL;
+ priv->sso_available = FALSE;
+ }
+ } else {
+ g_warning ("NTLM single-sign-on using %s failed", NTLM_AUTH);
+ }
+ }
+ /* If NTLM single-sign-on fails, go back to original
+ * request handling process.
+ */
+#endif
+ header = soup_ntlm_request ();
+ conn->state = SOUP_NTLM_SENT_REQUEST;
+ break;
+ case SOUP_NTLM_RECEIVED_CHALLENGE:
+ if (conn->response_header) {
+ header = conn->response_header;
+ conn->response_header = NULL;
+ } else {
+ header = soup_ntlm_response (conn->nonce,
+ priv->username,
+ priv->nt_hash,
+ priv->lm_hash,
+ NULL,
+ priv->domain);
+ }
+ g_clear_pointer (&conn->nonce, g_free);
+ conn->state = SOUP_NTLM_SENT_RESPONSE;
+
+ if (priv->password_state != SOUP_NTLM_PASSWORD_ACCEPTED) {
+ /* We need to know if this worked */
+ g_signal_connect (msg, "got-headers",
+ G_CALLBACK (got_final_auth_result),
+ auth);
+ }
+ break;
+#ifdef USE_NTLM_AUTH
+ case SOUP_NTLM_SSO_FAILED:
+ /* Restart request without SSO */
+ g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
+ priv->sso_available = FALSE;
+ header = soup_ntlm_request ();
+ conn->state = SOUP_NTLM_SENT_REQUEST;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return header;
}
static void
soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
{
SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_ntlm_class);
+ SoupConnectionAuthClass *connauth_class = SOUP_CONNECTION_AUTH_CLASS (auth_ntlm_class);
GObjectClass *object_class = G_OBJECT_CLASS (auth_ntlm_class);
g_type_class_add_private (auth_ntlm_class, sizeof (SoupAuthNTLMPrivate));
@@ -101,27 +542,721 @@ soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
auth_class->scheme_name = "NTLM";
auth_class->strength = 3;
- auth_class->update = soup_auth_ntlm_update;
auth_class->get_protection_space = soup_auth_ntlm_get_protection_space;
auth_class->authenticate = soup_auth_ntlm_authenticate;
auth_class->is_authenticated = soup_auth_ntlm_is_authenticated;
- auth_class->get_authorization = soup_auth_ntlm_get_authorization;
+
+ connauth_class->create_connection_state = soup_auth_ntlm_create_connection_state;
+ connauth_class->free_connection_state = soup_auth_ntlm_free_connection_state;
+ connauth_class->update_connection = soup_auth_ntlm_update_connection;
+ connauth_class->get_connection_authorization = soup_auth_ntlm_get_connection_authorization;
+ connauth_class->is_connection_ready = soup_auth_ntlm_is_connection_ready;
object_class->finalize = soup_auth_ntlm_finalize;
+
+#ifdef USE_NTLM_AUTH
+ ntlm_auth_available = g_file_test (NTLM_AUTH, G_FILE_TEST_IS_EXECUTABLE);
+ ntlm_auth_debug = (g_getenv ("SOUP_NTLM_AUTH_DEBUG") != NULL);
+#endif
+}
+
+static void md4sum (const unsigned char *in,
+ int nbytes,
+ unsigned char digest[16]);
+
+typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */
+
+static void deskey (DES_KS, unsigned char *, int);
+
+static void des (DES_KS, unsigned char *);
+
+static void setup_schedule (const guchar *key_56, DES_KS ks);
+
+static void calc_response (const guchar *key,
+ const guchar *plaintext,
+ guchar *results);
+
+#define LM_PASSWORD_MAGIC "\x4B\x47\x53\x21\x40\x23\x24\x25" \
+ "\x4B\x47\x53\x21\x40\x23\x24\x25" \
+ "\x00\x00\x00\x00\x00"
+
+static void
+soup_ntlm_lanmanager_hash (const char *password, guchar hash[21])
+{
+ guchar lm_password [15];
+ DES_KS ks;
+ int i;
+
+ for (i = 0; i < 14 && password [i]; i++)
+ lm_password [i] = g_ascii_toupper ((unsigned char) password [i]);
+
+ for (; i < 15; i++)
+ lm_password [i] = '\0';
+
+ memcpy (hash, LM_PASSWORD_MAGIC, 21);
+
+ setup_schedule (lm_password, ks);
+ des (ks, hash);
+
+ setup_schedule (lm_password + 7, ks);
+ des (ks, hash + 8);
}
-const char *
-soup_auth_ntlm_get_username (SoupAuth *auth)
+static void
+soup_ntlm_nt_hash (const char *password, guchar hash[21])
{
- SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ unsigned char *buf, *p;
+
+ p = buf = g_malloc (strlen (password) * 2);
+
+ while (*password) {
+ *p++ = *password++;
+ *p++ = '\0';
+ }
+
+ md4sum (buf, p - buf, hash);
+ memset (hash + 16, 0, 5);
- return priv->username;
+ g_free (buf);
}
-const char *
-soup_auth_ntlm_get_password (SoupAuth *auth)
+typedef struct {
+ guint16 length;
+ guint16 length2;
+ guint16 offset;
+ guchar zero_pad[2];
+} NTLMString;
+
+#define NTLM_CHALLENGE_NONCE_OFFSET 24
+#define NTLM_CHALLENGE_NONCE_LENGTH 8
+#define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
+
+#define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
+#define NTLM_RESPONSE_FLAGS 0x8201
+
+typedef struct {
+ guchar header[12];
+
+ NTLMString lm_resp;
+ NTLMString nt_resp;
+ NTLMString domain;
+ NTLMString user;
+ NTLMString host;
+ NTLMString session_key;
+
+ guint32 flags;
+} NTLMResponse;
+
+static void
+ntlm_set_string (NTLMString *string, int *offset, int len)
{
- SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ string->offset = GUINT16_TO_LE (*offset);
+ string->length = string->length2 = GUINT16_TO_LE (len);
+ *offset += len;
+}
+
+static char *
+soup_ntlm_request (void)
+{
+ return g_strdup ("NTLM TlRMTVNTUAABAAAABYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
+}
+
+static gboolean
+soup_ntlm_parse_challenge (const char *challenge,
+ char **nonce,
+ char **default_domain)
+{
+ gsize clen;
+ NTLMString domain;
+ guchar *chall;
+
+ chall = g_base64_decode (challenge, &clen);
+ if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
+ clen < NTLM_CHALLENGE_NONCE_OFFSET + NTLM_CHALLENGE_NONCE_LENGTH) {
+ g_free (chall);
+ return FALSE;
+ }
+
+ if (default_domain) {
+ memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
+ domain.length = GUINT16_FROM_LE (domain.length);
+ domain.offset = GUINT16_FROM_LE (domain.offset);
+
+ if (clen < domain.length + domain.offset) {
+ g_free (chall);
+ return FALSE;
+ }
+
+ *default_domain = g_convert ((char *)chall + domain.offset,
+ domain.length, "UTF-8", "UCS-2LE",
+ NULL, NULL, NULL);
+ }
+
+ if (nonce) {
+ *nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
+ NTLM_CHALLENGE_NONCE_LENGTH);
+ }
+
+ g_free (chall);
+ return TRUE;
+}
+
+static char *
+soup_ntlm_response (const char *nonce,
+ const char *user,
+ guchar nt_hash[21],
+ guchar lm_hash[21],
+ const char *host,
+ const char *domain)
+{
+ int offset;
+ gsize hlen, dlen, ulen;
+ guchar lm_resp[24], nt_resp[24];
+ char *user_conv, *host_conv, *domain_conv;
+ NTLMResponse resp;
+ char *out, *p;
+ int state, save;
+
+ calc_response (nt_hash, (guchar *)nonce, nt_resp);
+ calc_response (lm_hash, (guchar *)nonce, lm_resp);
+
+ memset (&resp, 0, sizeof (resp));
+ memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
+ resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
+
+ offset = sizeof (resp);
+
+ if (!host)
+ host = "UNKNOWN";
+
+ domain_conv = g_convert (domain, -1, "UCS-2LE", "UTF-8", NULL, &dlen, NULL);
+ user_conv = g_convert (user, -1, "UCS-2LE", "UTF-8", NULL, &ulen, NULL);
+ host_conv = g_convert (host, -1, "UCS-2LE", "UTF-8", NULL, &hlen, NULL);
+
+ ntlm_set_string (&resp.domain, &offset, dlen);
+ ntlm_set_string (&resp.user, &offset, ulen);
+ ntlm_set_string (&resp.host, &offset, hlen);
+ ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
+ ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
+
+ out = g_malloc (((offset + 3) * 4) / 3 + 6);
+ strncpy (out, "NTLM ", 5);
+ p = out + 5;
+
+ state = save = 0;
+
+ p += g_base64_encode_step ((const guchar *) &resp, sizeof (resp),
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step ((const guchar *) domain_conv, dlen,
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step ((const guchar *) user_conv, ulen,
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step ((const guchar *) host_conv, hlen,
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step (lm_resp, sizeof (lm_resp),
+ FALSE, p, &state, &save);
+ p += g_base64_encode_step (nt_resp, sizeof (nt_resp),
+ FALSE, p, &state, &save);
+ p += g_base64_encode_close (FALSE, p, &state, &save);
+ *p = '\0';
+
+ g_free (domain_conv);
+ g_free (user_conv);
+ g_free (host_conv);
+
+ return out;
+}
+
+/* DES utils */
+/* Set up a key schedule based on a 56bit key */
+static void
+setup_schedule (const guchar *key_56, DES_KS ks)
+{
+ guchar key[8];
+ int i, c, bit;
+
+ key[0] = (key_56[0]) ;
+ key[1] = (key_56[1] >> 1) | ((key_56[0] << 7) & 0xFF);
+ key[2] = (key_56[2] >> 2) | ((key_56[1] << 6) & 0xFF);
+ key[3] = (key_56[3] >> 3) | ((key_56[2] << 5) & 0xFF);
+ key[4] = (key_56[4] >> 4) | ((key_56[3] << 4) & 0xFF);
+ key[5] = (key_56[5] >> 5) | ((key_56[4] << 3) & 0xFF);
+ key[6] = (key_56[6] >> 6) | ((key_56[5] << 2) & 0xFF);
+ key[7] = ((key_56[6] << 1) & 0xFF);
+
+ /* Fix parity */
+ for (i = 0; i < 8; i++) {
+ for (c = bit = 0; bit < 8; bit++)
+ if (key[i] & (1 << bit))
+ c++;
+ if (!(c & 1))
+ key[i] ^= 0x01;
+ }
+
+ deskey (ks, key, 0);
+}
+
+static void
+calc_response (const guchar *key, const guchar *plaintext, guchar *results)
+{
+ DES_KS ks;
+
+ memcpy (results, plaintext, 8);
+ memcpy (results + 8, plaintext, 8);
+ memcpy (results + 16, plaintext, 8);
+
+ setup_schedule (key, ks);
+ des (ks, results);
+
+ setup_schedule (key + 7, ks);
+ des (ks, results + 8);
+
+ setup_schedule (key + 14, ks);
+ des (ks, results + 16);
+}
- return priv->password;
+
+/*
+ * MD4 encoder. (The one everyone else uses is not GPL-compatible;
+ * this is a reimplementation from spec.) This doesn't need to be
+ * efficient for our purposes, although it would be nice to fix
+ * it to not malloc()...
+ */
+
+#define F(X,Y,Z) ( ((X)&(Y)) | ((~(X))&(Z)) )
+#define G(X,Y,Z) ( ((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)) )
+#define H(X,Y,Z) ( (X)^(Y)^(Z) )
+#define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) )
+
+static void
+md4sum (const unsigned char *in, int nbytes, unsigned char digest[16])
+{
+ unsigned char *M;
+ guint32 A, B, C, D, AA, BB, CC, DD, X[16];
+ int pbytes, nbits = nbytes * 8, i, j;
+
+ pbytes = (120 - (nbytes % 64)) % 64;
+ M = alloca (nbytes + pbytes + 8);
+ memcpy (M, in, nbytes);
+ memset (M + nbytes, 0, pbytes + 8);
+ M[nbytes] = 0x80;
+ M[nbytes + pbytes] = nbits & 0xFF;
+ M[nbytes + pbytes + 1] = (nbits >> 8) & 0xFF;
+ M[nbytes + pbytes + 2] = (nbits >> 16) & 0xFF;
+ M[nbytes + pbytes + 3] = (nbits >> 24) & 0xFF;
+
+ A = 0x67452301;
+ B = 0xEFCDAB89;
+ C = 0x98BADCFE;
+ D = 0x10325476;
+
+ for (i = 0; i < nbytes + pbytes + 8; i += 64) {
+ for (j = 0; j < 16; j++) {
+ X[j] = (M[i + j*4]) |
+ (M[i + j*4 + 1] << 8) |
+ (M[i + j*4 + 2] << 16) |
+ (M[i + j*4 + 3] << 24);
+ }
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+
+ A = ROT (A + F(B, C, D) + X[0], 3);
+ D = ROT (D + F(A, B, C) + X[1], 7);
+ C = ROT (C + F(D, A, B) + X[2], 11);
+ B = ROT (B + F(C, D, A) + X[3], 19);
+ A = ROT (A + F(B, C, D) + X[4], 3);
+ D = ROT (D + F(A, B, C) + X[5], 7);
+ C = ROT (C + F(D, A, B) + X[6], 11);
+ B = ROT (B + F(C, D, A) + X[7], 19);
+ A = ROT (A + F(B, C, D) + X[8], 3);
+ D = ROT (D + F(A, B, C) + X[9], 7);
+ C = ROT (C + F(D, A, B) + X[10], 11);
+ B = ROT (B + F(C, D, A) + X[11], 19);
+ A = ROT (A + F(B, C, D) + X[12], 3);
+ D = ROT (D + F(A, B, C) + X[13], 7);
+ C = ROT (C + F(D, A, B) + X[14], 11);
+ B = ROT (B + F(C, D, A) + X[15], 19);
+
+ A = ROT (A + G(B, C, D) + X[0] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[4] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[8] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[12] + 0x5A827999, 13);
+ A = ROT (A + G(B, C, D) + X[1] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[5] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[9] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[13] + 0x5A827999, 13);
+ A = ROT (A + G(B, C, D) + X[2] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[6] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[10] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[14] + 0x5A827999, 13);
+ A = ROT (A + G(B, C, D) + X[3] + 0x5A827999, 3);
+ D = ROT (D + G(A, B, C) + X[7] + 0x5A827999, 5);
+ C = ROT (C + G(D, A, B) + X[11] + 0x5A827999, 9);
+ B = ROT (B + G(C, D, A) + X[15] + 0x5A827999, 13);
+
+ A = ROT (A + H(B, C, D) + X[0] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[8] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[4] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[12] + 0x6ED9EBA1, 15);
+ A = ROT (A + H(B, C, D) + X[2] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[10] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[6] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[14] + 0x6ED9EBA1, 15);
+ A = ROT (A + H(B, C, D) + X[1] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[9] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[5] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[13] + 0x6ED9EBA1, 15);
+ A = ROT (A + H(B, C, D) + X[3] + 0x6ED9EBA1, 3);
+ D = ROT (D + H(A, B, C) + X[11] + 0x6ED9EBA1, 9);
+ C = ROT (C + H(D, A, B) + X[7] + 0x6ED9EBA1, 11);
+ B = ROT (B + H(C, D, A) + X[15] + 0x6ED9EBA1, 15);
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+ }
+
+ digest[0] = A & 0xFF;
+ digest[1] = (A >> 8) & 0xFF;
+ digest[2] = (A >> 16) & 0xFF;
+ digest[3] = (A >> 24) & 0xFF;
+ digest[4] = B & 0xFF;
+ digest[5] = (B >> 8) & 0xFF;
+ digest[6] = (B >> 16) & 0xFF;
+ digest[7] = (B >> 24) & 0xFF;
+ digest[8] = C & 0xFF;
+ digest[9] = (C >> 8) & 0xFF;
+ digest[10] = (C >> 16) & 0xFF;
+ digest[11] = (C >> 24) & 0xFF;
+ digest[12] = D & 0xFF;
+ digest[13] = (D >> 8) & 0xFF;
+ digest[14] = (D >> 16) & 0xFF;
+ digest[15] = (D >> 24) & 0xFF;
+}
+
+
+/* Public domain DES implementation from Phil Karn */
+static const guint32 Spbox[8][64] = {
+ { 0x01010400,0x00000000,0x00010000,0x01010404,
+ 0x01010004,0x00010404,0x00000004,0x00010000,
+ 0x00000400,0x01010400,0x01010404,0x00000400,
+ 0x01000404,0x01010004,0x01000000,0x00000004,
+ 0x00000404,0x01000400,0x01000400,0x00010400,
+ 0x00010400,0x01010000,0x01010000,0x01000404,
+ 0x00010004,0x01000004,0x01000004,0x00010004,
+ 0x00000000,0x00000404,0x00010404,0x01000000,
+ 0x00010000,0x01010404,0x00000004,0x01010000,
+ 0x01010400,0x01000000,0x01000000,0x00000400,
+ 0x01010004,0x00010000,0x00010400,0x01000004,
+ 0x00000400,0x00000004,0x01000404,0x00010404,
+ 0x01010404,0x00010004,0x01010000,0x01000404,
+ 0x01000004,0x00000404,0x00010404,0x01010400,
+ 0x00000404,0x01000400,0x01000400,0x00000000,
+ 0x00010004,0x00010400,0x00000000,0x01010004 },
+ { 0x80108020,0x80008000,0x00008000,0x00108020,
+ 0x00100000,0x00000020,0x80100020,0x80008020,
+ 0x80000020,0x80108020,0x80108000,0x80000000,
+ 0x80008000,0x00100000,0x00000020,0x80100020,
+ 0x00108000,0x00100020,0x80008020,0x00000000,
+ 0x80000000,0x00008000,0x00108020,0x80100000,
+ 0x00100020,0x80000020,0x00000000,0x00108000,
+ 0x00008020,0x80108000,0x80100000,0x00008020,
+ 0x00000000,0x00108020,0x80100020,0x00100000,
+ 0x80008020,0x80100000,0x80108000,0x00008000,
+ 0x80100000,0x80008000,0x00000020,0x80108020,
+ 0x00108020,0x00000020,0x00008000,0x80000000,
+ 0x00008020,0x80108000,0x00100000,0x80000020,
+ 0x00100020,0x80008020,0x80000020,0x00100020,
+ 0x00108000,0x00000000,0x80008000,0x00008020,
+ 0x80000000,0x80100020,0x80108020,0x00108000 },
+ { 0x00000208,0x08020200,0x00000000,0x08020008,
+ 0x08000200,0x00000000,0x00020208,0x08000200,
+ 0x00020008,0x08000008,0x08000008,0x00020000,
+ 0x08020208,0x00020008,0x08020000,0x00000208,
+ 0x08000000,0x00000008,0x08020200,0x00000200,
+ 0x00020200,0x08020000,0x08020008,0x00020208,
+ 0x08000208,0x00020200,0x00020000,0x08000208,
+ 0x00000008,0x08020208,0x00000200,0x08000000,
+ 0x08020200,0x08000000,0x00020008,0x00000208,
+ 0x00020000,0x08020200,0x08000200,0x00000000,
+ 0x00000200,0x00020008,0x08020208,0x08000200,
+ 0x08000008,0x00000200,0x00000000,0x08020008,
+ 0x08000208,0x00020000,0x08000000,0x08020208,
+ 0x00000008,0x00020208,0x00020200,0x08000008,
+ 0x08020000,0x08000208,0x00000208,0x08020000,
+ 0x00020208,0x00000008,0x08020008,0x00020200 },
+ { 0x00802001,0x00002081,0x00002081,0x00000080,
+ 0x00802080,0x00800081,0x00800001,0x00002001,
+ 0x00000000,0x00802000,0x00802000,0x00802081,
+ 0x00000081,0x00000000,0x00800080,0x00800001,
+ 0x00000001,0x00002000,0x00800000,0x00802001,
+ 0x00000080,0x00800000,0x00002001,0x00002080,
+ 0x00800081,0x00000001,0x00002080,0x00800080,
+ 0x00002000,0x00802080,0x00802081,0x00000081,
+ 0x00800080,0x00800001,0x00802000,0x00802081,
+ 0x00000081,0x00000000,0x00000000,0x00802000,
+ 0x00002080,0x00800080,0x00800081,0x00000001,
+ 0x00802001,0x00002081,0x00002081,0x00000080,
+ 0x00802081,0x00000081,0x00000001,0x00002000,
+ 0x00800001,0x00002001,0x00802080,0x00800081,
+ 0x00002001,0x00002080,0x00800000,0x00802001,
+ 0x00000080,0x00800000,0x00002000,0x00802080 },
+ { 0x00000100,0x02080100,0x02080000,0x42000100,
+ 0x00080000,0x00000100,0x40000000,0x02080000,
+ 0x40080100,0x00080000,0x02000100,0x40080100,
+ 0x42000100,0x42080000,0x00080100,0x40000000,
+ 0x02000000,0x40080000,0x40080000,0x00000000,
+ 0x40000100,0x42080100,0x42080100,0x02000100,
+ 0x42080000,0x40000100,0x00000000,0x42000000,
+ 0x02080100,0x02000000,0x42000000,0x00080100,
+ 0x00080000,0x42000100,0x00000100,0x02000000,
+ 0x40000000,0x02080000,0x42000100,0x40080100,
+ 0x02000100,0x40000000,0x42080000,0x02080100,
+ 0x40080100,0x00000100,0x02000000,0x42080000,
+ 0x42080100,0x00080100,0x42000000,0x42080100,
+ 0x02080000,0x00000000,0x40080000,0x42000000,
+ 0x00080100,0x02000100,0x40000100,0x00080000,
+ 0x00000000,0x40080000,0x02080100,0x40000100 },
+ { 0x20000010,0x20400000,0x00004000,0x20404010,
+ 0x20400000,0x00000010,0x20404010,0x00400000,
+ 0x20004000,0x00404010,0x00400000,0x20000010,
+ 0x00400010,0x20004000,0x20000000,0x00004010,
+ 0x00000000,0x00400010,0x20004010,0x00004000,
+ 0x00404000,0x20004010,0x00000010,0x20400010,
+ 0x20400010,0x00000000,0x00404010,0x20404000,
+ 0x00004010,0x00404000,0x20404000,0x20000000,
+ 0x20004000,0x00000010,0x20400010,0x00404000,
+ 0x20404010,0x00400000,0x00004010,0x20000010,
+ 0x00400000,0x20004000,0x20000000,0x00004010,
+ 0x20000010,0x20404010,0x00404000,0x20400000,
+ 0x00404010,0x20404000,0x00000000,0x20400010,
+ 0x00000010,0x00004000,0x20400000,0x00404010,
+ 0x00004000,0x00400010,0x20004010,0x00000000,
+ 0x20404000,0x20000000,0x00400010,0x20004010 },
+ { 0x00200000,0x04200002,0x04000802,0x00000000,
+ 0x00000800,0x04000802,0x00200802,0x04200800,
+ 0x04200802,0x00200000,0x00000000,0x04000002,
+ 0x00000002,0x04000000,0x04200002,0x00000802,
+ 0x04000800,0x00200802,0x00200002,0x04000800,
+ 0x04000002,0x04200000,0x04200800,0x00200002,
+ 0x04200000,0x00000800,0x00000802,0x04200802,
+ 0x00200800,0x00000002,0x04000000,0x00200800,
+ 0x04000000,0x00200800,0x00200000,0x04000802,
+ 0x04000802,0x04200002,0x04200002,0x00000002,
+ 0x00200002,0x04000000,0x04000800,0x00200000,
+ 0x04200800,0x00000802,0x00200802,0x04200800,
+ 0x00000802,0x04000002,0x04200802,0x04200000,
+ 0x00200800,0x00000000,0x00000002,0x04200802,
+ 0x00000000,0x00200802,0x04200000,0x00000800,
+ 0x04000002,0x04000800,0x00000800,0x00200002 },
+ { 0x10001040,0x00001000,0x00040000,0x10041040,
+ 0x10000000,0x10001040,0x00000040,0x10000000,
+ 0x00040040,0x10040000,0x10041040,0x00041000,
+ 0x10041000,0x00041040,0x00001000,0x00000040,
+ 0x10040000,0x10000040,0x10001000,0x00001040,
+ 0x00041000,0x00040040,0x10040040,0x10041000,
+ 0x00001040,0x00000000,0x00000000,0x10040040,
+ 0x10000040,0x10001000,0x00041040,0x00040000,
+ 0x00041040,0x00040000,0x10041000,0x00001000,
+ 0x00000040,0x10040040,0x00001000,0x00041040,
+ 0x10001000,0x00000040,0x10000040,0x10040000,
+ 0x10040040,0x10000000,0x00040000,0x10001040,
+ 0x00000000,0x10041040,0x00040040,0x10000040,
+ 0x10040000,0x10001000,0x10001040,0x00000000,
+ 0x10041040,0x00041000,0x00041000,0x00001040,
+ 0x00001040,0x00040040,0x10000000,0x10041000 }
+};
+
+#undef F
+#define F(l,r,key){\
+ work = ((r >> 4) | (r << 28)) ^ key[0];\
+ l ^= Spbox[6][work & 0x3f];\
+ l ^= Spbox[4][(work >> 8) & 0x3f];\
+ l ^= Spbox[2][(work >> 16) & 0x3f];\
+ l ^= Spbox[0][(work >> 24) & 0x3f];\
+ work = r ^ key[1];\
+ l ^= Spbox[7][work & 0x3f];\
+ l ^= Spbox[5][(work >> 8) & 0x3f];\
+ l ^= Spbox[3][(work >> 16) & 0x3f];\
+ l ^= Spbox[1][(work >> 24) & 0x3f];\
+}
+/* Encrypt or decrypt a block of data in ECB mode */
+static void
+des (guint32 ks[16][2], unsigned char block[8])
+{
+ guint32 left,right,work;
+
+ /* Read input block and place in left/right in big-endian order */
+ left = ((guint32)block[0] << 24)
+ | ((guint32)block[1] << 16)
+ | ((guint32)block[2] << 8)
+ | (guint32)block[3];
+ right = ((guint32)block[4] << 24)
+ | ((guint32)block[5] << 16)
+ | ((guint32)block[6] << 8)
+ | (guint32)block[7];
+
+ /* Hoey's clever initial permutation algorithm, from Outerbridge
+ * (see Schneier p 478)
+ *
+ * The convention here is the same as Outerbridge: rotate each
+ * register left by 1 bit, i.e., so that "left" contains permuted
+ * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32
+ * (using origin-1 numbering as in the FIPS). This allows us to avoid
+ * one of the two rotates that would otherwise be required in each of
+ * the 16 rounds.
+ */
+ work = ((left >> 4) ^ right) & 0x0f0f0f0f;
+ right ^= work;
+ left ^= work << 4;
+ work = ((left >> 16) ^ right) & 0xffff;
+ right ^= work;
+ left ^= work << 16;
+ work = ((right >> 2) ^ left) & 0x33333333;
+ left ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ left) & 0xff00ff;
+ left ^= work;
+ right ^= (work << 8);
+ right = (right << 1) | (right >> 31);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 1) | (left >> 31);
+
+ /* Now do the 16 rounds */
+ F(left,right,ks[0]);
+ F(right,left,ks[1]);
+ F(left,right,ks[2]);
+ F(right,left,ks[3]);
+ F(left,right,ks[4]);
+ F(right,left,ks[5]);
+ F(left,right,ks[6]);
+ F(right,left,ks[7]);
+ F(left,right,ks[8]);
+ F(right,left,ks[9]);
+ F(left,right,ks[10]);
+ F(right,left,ks[11]);
+ F(left,right,ks[12]);
+ F(right,left,ks[13]);
+ F(left,right,ks[14]);
+ F(right,left,ks[15]);
+
+ /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
+ right = (right << 31) | (right >> 1);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left >> 1) | (left << 31);
+ work = ((left >> 8) ^ right) & 0xff00ff;
+ right ^= work;
+ left ^= work << 8;
+ work = ((left >> 2) ^ right) & 0x33333333;
+ right ^= work;
+ left ^= work << 2;
+ work = ((right >> 16) ^ left) & 0xffff;
+ left ^= work;
+ right ^= work << 16;
+ work = ((right >> 4) ^ left) & 0x0f0f0f0f;
+ left ^= work;
+ right ^= work << 4;
+
+ /* Put the block back into the user's buffer with final swap */
+ block[0] = right >> 24;
+ block[1] = right >> 16;
+ block[2] = right >> 8;
+ block[3] = right;
+ block[4] = left >> 24;
+ block[5] = left >> 16;
+ block[6] = left >> 8;
+ block[7] = left;
+}
+
+/* Key schedule-related tables from FIPS-46 */
+
+/* permuted choice table (key) */
+static const unsigned char pc1[] = {
+ 57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4
+};
+
+/* number left rotations of pc1 */
+static const unsigned char totrot[] = {
+ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
+};
+
+/* permuted choice key (table) */
+static const unsigned char pc2[] = {
+ 14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32
+};
+
+/* End of DES-defined tables */
+
+
+/* bit 0 is left-most in byte */
+static const int bytebit[] = {
+ 0200,0100,040,020,010,04,02,01
+};
+
+
+/* Generate key schedule for encryption or decryption
+ * depending on the value of "decrypt"
+ */
+static void
+deskey (DES_KS k, unsigned char *key, int decrypt)
+{
+ unsigned char pc1m[56]; /* place to modify pc1 into */
+ unsigned char pcr[56]; /* place to rotate pc1 into */
+ register int i,j,l;
+ int m;
+ unsigned char ks[8];
+
+ for (j=0; j<56; j++) { /* convert pc1 to bits of key */
+ l=pc1[j]-1; /* integer bit location */
+ m = l & 07; /* find bit */
+ pc1m[j]=(key[l>>3] & /* find which key byte l is in */
+ bytebit[m]) /* and which bit of that byte */
+ ? 1 : 0; /* and store 1-bit result */
+ }
+ for (i=0; i<16; i++) { /* key chunk for each iteration */
+ memset(ks,0,sizeof(ks)); /* Clear key schedule */
+ for (j=0; j<56; j++) /* rotate pc1 the right amount */
+ pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
+ /* rotate left and right halves independently */
+ for (j=0; j<48; j++){ /* select bits individually */
+ /* check bit that goes to ks[j] */
+ if (pcr[pc2[j]-1]){
+ /* mask it in if it's there */
+ l= j % 6;
+ ks[j/6] |= bytebit[l] >> 2;
+ }
+ }
+ /* Now convert to packed odd/even interleaved form */
+ k[i][0] = ((guint32)ks[0] << 24)
+ | ((guint32)ks[2] << 16)
+ | ((guint32)ks[4] << 8)
+ | ((guint32)ks[6]);
+ k[i][1] = ((guint32)ks[1] << 24)
+ | ((guint32)ks[3] << 16)
+ | ((guint32)ks[5] << 8)
+ | ((guint32)ks[7]);
+ }
}
diff --git a/libsoup/soup-auth-ntlm.h b/libsoup/soup-auth-ntlm.h
index 1f56976b..43c40856 100644
--- a/libsoup/soup-auth-ntlm.h
+++ b/libsoup/soup-auth-ntlm.h
@@ -6,7 +6,7 @@
#ifndef SOUP_AUTH_NTLM_H
#define SOUP_AUTH_NTLM_H 1
-#include "soup-auth.h"
+#include "soup-connection-auth.h"
#define SOUP_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLM))
#define SOUP_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
@@ -15,18 +15,13 @@
#define SOUP_AUTH_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
typedef struct {
- SoupAuth parent;
+ SoupConnectionAuth parent;
} SoupAuthNTLM;
typedef struct {
- SoupAuthClass parent_class;
+ SoupConnectionAuthClass parent_class;
} SoupAuthNTLMClass;
-SoupAuth *soup_auth_ntlm_new (const char *realm,
- const char *host);
-const char *soup_auth_ntlm_get_username (SoupAuth *auth);
-const char *soup_auth_ntlm_get_password (SoupAuth *auth);
-
#endif /* SOUP_AUTH_NTLM_H */
diff --git a/libsoup/soup-auth.c b/libsoup/soup-auth.c
index 6960e520..7c34dcfa 100644
--- a/libsoup/soup-auth.c
+++ b/libsoup/soup-auth.c
@@ -9,14 +9,11 @@
#include <config.h>
#endif
-#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-
#include <string.h>
#include "soup-auth.h"
-#include "soup-headers.h"
-#include "soup-marshal.h"
-#include "soup-uri.h"
+#include "soup.h"
+#include "soup-connection-auth.h"
/**
* SECTION:soup-auth
@@ -40,21 +37,12 @@
typedef struct {
gboolean proxy;
char *host;
-
- GHashTable *saved_passwords;
} SoupAuthPrivate;
#define SOUP_AUTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH, SoupAuthPrivate))
G_DEFINE_ABSTRACT_TYPE (SoupAuth, soup_auth, G_TYPE_OBJECT)
enum {
- SAVE_PASSWORD,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-enum {
PROP_0,
PROP_SCHEME_NAME,
@@ -79,8 +67,6 @@ soup_auth_finalize (GObject *object)
g_free (auth->realm);
g_free (priv->host);
- if (priv->saved_passwords)
- g_hash_table_destroy (priv->saved_passwords);
G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
}
@@ -120,9 +106,13 @@ soup_auth_get_property (GObject *object, guint prop_id,
g_value_set_string (value, soup_auth_get_scheme_name (auth));
break;
case PROP_REALM:
+ if (auth->realm)
+ g_free (auth->realm);
g_value_set_string (value, soup_auth_get_realm (auth));
break;
case PROP_HOST:
+ if (priv->host)
+ g_free (priv->host);
g_value_set_string (value, soup_auth_get_host (auth));
break;
case PROP_IS_FOR_PROXY:
@@ -149,30 +139,6 @@ soup_auth_class_init (SoupAuthClass *auth_class)
object_class->set_property = soup_auth_set_property;
object_class->get_property = soup_auth_get_property;
- /**
- * SoupAuth::save-password:
- * @auth: the auth
- * @username: the username to save
- * @password: the password to save
- *
- * Emitted to request that the @username/@password pair be
- * saved. If the session supports password-saving, it will
- * connect to this signal before emitting
- * #SoupSession::authenticate, so that it record the password
- * if requested by the caller.
- *
- * Since: 2.28
- **/
- signals[SAVE_PASSWORD] =
- g_signal_new ("save-password",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL,
- _soup_marshal_NONE__STRING_STRING,
- G_TYPE_NONE, 2,
- G_TYPE_STRING,
- G_TYPE_STRING);
-
/* properties */
/**
* SOUP_AUTH_SCHEME_NAME:
@@ -199,7 +165,7 @@ soup_auth_class_init (SoupAuthClass *auth_class)
"Realm",
"Authentication realm",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ G_PARAM_READWRITE));
/**
* SOUP_AUTH_HOST:
*
@@ -212,7 +178,7 @@ soup_auth_class_init (SoupAuthClass *auth_class)
"Host",
"Authentication host",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ G_PARAM_READWRITE));
/**
* SOUP_AUTH_IS_FOR_PROXY:
*
@@ -225,7 +191,7 @@ soup_auth_class_init (SoupAuthClass *auth_class)
"For Proxy",
"Whether or not the auth is for a proxy server",
FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ G_PARAM_READWRITE));
/**
* SOUP_AUTH_IS_AUTHENTICATED:
*
@@ -279,19 +245,12 @@ soup_auth_new (GType type, SoupMessage *msg, const char *auth_header)
}
params = soup_header_parse_param_list (auth_header + strlen (scheme));
- if (!params) {
- g_object_unref (auth);
- return NULL;
- }
+ if (!params)
+ params = g_hash_table_new (NULL, NULL);
realm = g_hash_table_lookup (params, "realm");
- if (!realm) {
- soup_header_free_param_list (params);
- g_object_unref (auth);
- return NULL;
- }
-
- auth->realm = g_strdup (realm);
+ if (realm)
+ auth->realm = g_strdup (realm);
if (!SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params)) {
g_object_unref (auth);
@@ -332,10 +291,10 @@ soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
params = soup_header_parse_param_list (auth_header + strlen (scheme));
if (!params)
- return FALSE;
+ params = g_hash_table_new (NULL, NULL);
realm = g_hash_table_lookup (params, "realm");
- if (!realm || strcmp (realm, auth->realm) != 0) {
+ if (realm && auth->realm && strcmp (realm, auth->realm) != 0) {
soup_header_free_param_list (params);
return FALSE;
}
@@ -356,9 +315,6 @@ soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
*
* Call this on an auth to authenticate it; normally this will cause
* the auth's message to be requeued with the new authentication info.
- *
- * This does not cause the password to be saved to persistent storage;
- * see soup_auth_save_password() for that.
**/
void
soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
@@ -460,9 +416,13 @@ soup_auth_get_info (SoupAuth *auth)
{
g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
- return g_strdup_printf ("%s:%s",
- SOUP_AUTH_GET_CLASS (auth)->scheme_name,
- auth->realm);
+ if (SOUP_IS_CONNECTION_AUTH (auth))
+ return g_strdup (SOUP_AUTH_GET_CLASS (auth)->scheme_name);
+ else {
+ return g_strdup_printf ("%s:%s",
+ SOUP_AUTH_GET_CLASS (auth)->scheme_name,
+ auth->realm);
+ }
}
/**
@@ -502,6 +462,33 @@ soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
}
/**
+ * soup_auth_is_ready:
+ * @auth: a #SoupAuth
+ * @msg: a #SoupMessage
+ *
+ * Tests if @auth is ready to make a request for @msg with. For most
+ * auths, this is equivalent to soup_auth_is_authenticated(), but for
+ * some auth types (eg, NTLM), the auth may be sendable (eg, as an
+ * authentication request) even before it is authenticated.
+ *
+ * Return value: %TRUE if @auth is ready to make a request with.
+ *
+ * Since: 2.42
+ **/
+gboolean
+soup_auth_is_ready (SoupAuth *auth,
+ SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), TRUE);
+
+ if (SOUP_AUTH_GET_CLASS (auth)->is_ready)
+ return SOUP_AUTH_GET_CLASS (auth)->is_ready (auth, msg);
+ else
+ return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
+}
+
+/**
* soup_auth_get_protection_space:
* @auth: a #SoupAuth
* @source_uri: the URI of the request that @auth was generated in
@@ -539,134 +526,29 @@ soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
/**
* soup_auth_get_saved_users:
- * @auth: a #SoupAuth
- *
- * Gets a list of usernames for which a saved password is available.
- * (If the session is not configured to save passwords, this will
- * always be %NULL.)
- *
- * Return value: (transfer container): the list of usernames. You must
- * free the list with g_slist_free(), but do not free or modify the
- * contents.
*
- * Since: 2.28
- **/
+ * Return value: (transfer full) (element-type utf8):
+ */
GSList *
soup_auth_get_saved_users (SoupAuth *auth)
{
- SoupAuthPrivate *priv;
- GSList *users;
-
- g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
-
- priv = SOUP_AUTH_GET_PRIVATE (auth);
- users = NULL;
-
- if (priv->saved_passwords) {
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init (&iter, priv->saved_passwords);
- while (g_hash_table_iter_next (&iter, &key, &value))
- users = g_slist_prepend (users, key);
- }
- return users;
+ return NULL;
}
-/**
- * soup_auth_get_saved_password:
- * @auth: a #SoupAuth
- * @user: a username from the list returned from
- * soup_auth_get_saved_users().
- *
- * Given a username for which @auth has a saved password, this returns
- * that password. If @auth doesn't have a passwords saved for @user, it
- * returns %NULL.
- *
- * Return value: the saved password, or %NULL.
- *
- * Since: 2.28
- **/
const char *
soup_auth_get_saved_password (SoupAuth *auth, const char *user)
{
- SoupAuthPrivate *priv;
-
- g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
- g_return_val_if_fail (user != NULL, NULL);
-
- priv = SOUP_AUTH_GET_PRIVATE (auth);
- if (!priv->saved_passwords)
- return NULL;
- return g_hash_table_lookup (priv->saved_passwords, user);
-}
-
-static void
-free_password (gpointer password)
-{
- memset (password, 0, strlen (password));
- g_free (password);
+ return NULL;
}
-static inline void
-init_saved_passwords (SoupAuthPrivate *priv)
-{
- priv->saved_passwords = g_hash_table_new_full (
- g_str_hash, g_str_equal, g_free, free_password);
-}
-
-/**
- * soup_auth_has_saved_password:
- * @auth: a #SoupAuth
- * @username: a username
- * @password: a password
- *
- * Updates @auth to be aware of an already-saved username/password
- * combination. This method <emphasis>does not</emphasis> cause the
- * given @username and @password to be saved; use
- * soup_auth_save_password() for that. (soup_auth_has_saved_password()
- * is an internal method, which is used by the code that actually
- * saves and restores the passwords.)
- *
- * Since: 2.28
- **/
void
soup_auth_has_saved_password (SoupAuth *auth, const char *username,
const char *password)
{
- SoupAuthPrivate *priv;
-
- g_return_if_fail (SOUP_IS_AUTH (auth));
- g_return_if_fail (username != NULL);
- g_return_if_fail (password != NULL);
-
- priv = SOUP_AUTH_GET_PRIVATE (auth);
-
- if (!priv->saved_passwords)
- init_saved_passwords (priv);
- g_hash_table_insert (priv->saved_passwords,
- g_strdup (username), g_strdup (password));
}
-/**
- * soup_auth_save_password:
- * @auth: a #SoupAuth
- * @username: the username provided by the user or client
- * @password: the password provided by the user or client
- *
- * Requests that the username/password pair be saved to whatever form
- * of persistent password storage the session supports.
- *
- * Since: 2.28
- **/
void
soup_auth_save_password (SoupAuth *auth, const char *username,
const char *password)
{
- g_return_if_fail (SOUP_IS_AUTH (auth));
- g_return_if_fail (username != NULL);
- g_return_if_fail (password != NULL);
-
- g_signal_emit (auth, signals[SAVE_PASSWORD], 0,
- username, password);
}
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
index 18f11c7b..824857e2 100644
--- a/libsoup/soup-auth.h
+++ b/libsoup/soup-auth.h
@@ -44,8 +44,11 @@ typedef struct {
char * (*get_authorization) (SoupAuth *auth,
SoupMessage *msg);
+
+ gboolean (*is_ready) (SoupAuth *auth,
+ SoupMessage *msg);
+
/* Padding for future expansion */
- void (*_libsoup_reserved1) (void);
void (*_libsoup_reserved2) (void);
void (*_libsoup_reserved3) (void);
void (*_libsoup_reserved4) (void);
@@ -72,19 +75,13 @@ const char *soup_auth_get_host (SoupAuth *auth);
const char *soup_auth_get_realm (SoupAuth *auth);
char *soup_auth_get_info (SoupAuth *auth);
-#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-GSList *soup_auth_get_saved_users (SoupAuth *auth);
-const char *soup_auth_get_saved_password (SoupAuth *auth,
- const char *user);
-void soup_auth_save_password (SoupAuth *auth,
- const char *username,
- const char *password);
-#endif
-
void soup_auth_authenticate (SoupAuth *auth,
const char *username,
const char *password);
gboolean soup_auth_is_authenticated (SoupAuth *auth);
+SOUP_AVAILABLE_IN_2_42
+gboolean soup_auth_is_ready (SoupAuth *auth,
+ SoupMessage *msg);
char *soup_auth_get_authorization (SoupAuth *auth,
SoupMessage *msg);
@@ -94,12 +91,6 @@ GSList *soup_auth_get_protection_space (SoupAuth *auth,
void soup_auth_free_protection_space (SoupAuth *auth,
GSList *space);
-#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-void soup_auth_has_saved_password (SoupAuth *auth,
- const char *username,
- const char *password);
-#endif
-
/* The actual auth types, which can be added/removed as features */
#define SOUP_TYPE_AUTH_BASIC (soup_auth_basic_get_type ())
@@ -109,6 +100,25 @@ GType soup_auth_digest_get_type (void);
#define SOUP_TYPE_AUTH_NTLM (soup_auth_ntlm_get_type ())
GType soup_auth_ntlm_get_type (void);
+/* Deprecated SoupPasswordManager-related APIs: all are now no-ops */
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
+GSList *soup_auth_get_saved_users (SoupAuth *auth);
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
+const char *soup_auth_get_saved_password (SoupAuth *auth,
+ const char *user);
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
+void soup_auth_save_password (SoupAuth *auth,
+ const char *username,
+ const char *password);
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
+void soup_auth_has_saved_password (SoupAuth *auth,
+ const char *username,
+ const char *password);
+
G_END_DECLS
#endif /* SOUP_AUTH_H */
diff --git a/libsoup/soup-body-input-stream.c b/libsoup/soup-body-input-stream.c
index ba4ce6a6..62747446 100644
--- a/libsoup/soup-body-input-stream.c
+++ b/libsoup/soup-body-input-stream.c
@@ -16,7 +16,6 @@
#include "soup-body-input-stream.h"
#include "soup.h"
#include "soup-filter-input-stream.h"
-#include "soup-marshal.h"
typedef enum {
SOUP_BODY_INPUT_STREAM_STATE_CHUNK_SIZE,
@@ -33,6 +32,8 @@ struct _SoupBodyInputStreamPrivate {
goffset read_length;
SoupBodyInputStreamState chunked_state;
gboolean eof;
+
+ goffset pos;
};
enum {
@@ -50,10 +51,13 @@ enum {
};
static void soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
+static void soup_body_input_stream_seekable_init (GSeekableIface *seekable_interface);
G_DEFINE_TYPE_WITH_CODE (SoupBodyInputStream, soup_body_input_stream, G_TYPE_FILTER_INPUT_STREAM,
G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
- soup_body_input_stream_pollable_init))
+ soup_body_input_stream_pollable_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+ soup_body_input_stream_seekable_init))
static void
soup_body_input_stream_init (SoupBodyInputStream *bistream)
@@ -258,6 +262,9 @@ read_internal (GInputStream *stream,
blocking, cancellable, error);
if (bistream->priv->read_length != -1 && nread > 0)
bistream->priv->read_length -= nread;
+
+ if (bistream->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH)
+ bistream->priv->pos += nread;
return nread;
default:
@@ -266,6 +273,27 @@ read_internal (GInputStream *stream,
}
static gssize
+soup_body_input_stream_skip (GInputStream *stream,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupBodyInputStreamPrivate *priv = SOUP_BODY_INPUT_STREAM(stream)->priv;
+ gssize skipped;
+
+ skipped = g_input_stream_skip (G_FILTER_INPUT_STREAM (stream)->base_stream,
+ MIN (count, priv->read_length),
+ cancellable, error);
+
+ if (skipped == 0)
+ priv->eof = TRUE;
+ else if (skipped > 0)
+ priv->pos += skipped;
+
+ return skipped;
+}
+
+static gssize
soup_body_input_stream_read_fn (GInputStream *stream,
void *buffer,
gsize count,
@@ -295,6 +323,15 @@ soup_body_input_stream_is_readable (GPollableInputStream *stream)
g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (bistream->priv->base_stream));
}
+static gboolean
+soup_body_input_stream_can_poll (GPollableInputStream *pollable)
+{
+ GInputStream *base_stream = SOUP_BODY_INPUT_STREAM (pollable)->priv->base_stream;
+
+ return G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
+ g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream));
+}
+
static gssize
soup_body_input_stream_read_nonblocking (GPollableInputStream *stream,
void *buffer,
@@ -337,6 +374,7 @@ soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class)
object_class->set_property = soup_body_input_stream_set_property;
object_class->get_property = soup_body_input_stream_get_property;
+ input_stream_class->skip = soup_body_input_stream_skip;
input_stream_class->read_fn = soup_body_input_stream_read_fn;
input_stream_class->close_fn = soup_body_input_stream_close_fn;
@@ -346,7 +384,7 @@ soup_body_input_stream_class_init (SoupBodyInputStreamClass *stream_class)
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
g_object_class_install_property (
@@ -370,16 +408,107 @@ static void
soup_body_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
gpointer interface_data)
{
+ pollable_interface->can_poll = soup_body_input_stream_can_poll;
pollable_interface->is_readable = soup_body_input_stream_is_readable;
pollable_interface->read_nonblocking = soup_body_input_stream_read_nonblocking;
pollable_interface->create_source = soup_body_input_stream_create_source;
}
+static goffset
+soup_body_input_stream_tell (GSeekable *seekable)
+{
+ return SOUP_BODY_INPUT_STREAM (seekable)->priv->pos;
+}
+
+static gboolean
+soup_body_input_stream_can_seek (GSeekable *seekable)
+{
+ SoupBodyInputStreamPrivate *priv = SOUP_BODY_INPUT_STREAM (seekable)->priv;
+
+ return priv->encoding == SOUP_ENCODING_CONTENT_LENGTH
+ && G_IS_SEEKABLE (priv->base_stream)
+ && g_seekable_can_seek (G_SEEKABLE (priv->base_stream));
+}
+
+static gboolean
+soup_body_input_stream_seek (GSeekable *seekable,
+ goffset offset,
+ GSeekType type,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupBodyInputStreamPrivate *priv = SOUP_BODY_INPUT_STREAM (seekable)->priv;
+ goffset position, end_position;
+
+ end_position = priv->pos + priv->read_length;
+ switch (type) {
+ case G_SEEK_CUR:
+ position = priv->pos + offset;
+ break;
+ case G_SEEK_SET:
+ position = offset;
+ break;
+ case G_SEEK_END:
+ position = end_position + offset;
+ break;
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ if (position < 0 || position >= end_position) {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Invalid seek request"));
+ return FALSE;
+ }
+
+ if (!g_seekable_seek (G_SEEKABLE (priv->base_stream), position - priv->pos,
+ G_SEEK_CUR, cancellable, error))
+ return FALSE;
+
+ priv->pos = position;
+
+ return TRUE;
+}
+
+static gboolean
+soup_body_input_stream_can_truncate (GSeekable *seekable)
+{
+ return FALSE;
+}
+
+static gboolean
+soup_body_input_stream_truncate_fn (GSeekable *seekable,
+ goffset offset,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("Cannot truncate SoupBodyInputStream"));
+ return FALSE;
+}
+
+static void
+soup_body_input_stream_seekable_init (GSeekableIface *seekable_interface)
+{
+ seekable_interface->tell = soup_body_input_stream_tell;
+ seekable_interface->can_seek = soup_body_input_stream_can_seek;
+ seekable_interface->seek = soup_body_input_stream_seek;
+ seekable_interface->can_truncate = soup_body_input_stream_can_truncate;
+ seekable_interface->truncate_fn = soup_body_input_stream_truncate_fn;
+}
+
GInputStream *
-soup_body_input_stream_new (SoupFilterInputStream *base_stream,
- SoupEncoding encoding,
- goffset content_length)
+soup_body_input_stream_new (GInputStream *base_stream,
+ SoupEncoding encoding,
+ goffset content_length)
{
+ if (encoding == SOUP_ENCODING_CHUNKED)
+ g_return_val_if_fail (SOUP_IS_FILTER_INPUT_STREAM (base_stream), NULL);
+
return g_object_new (SOUP_TYPE_BODY_INPUT_STREAM,
"base-stream", base_stream,
"close-base-stream", FALSE,
diff --git a/libsoup/soup-body-input-stream.h b/libsoup/soup-body-input-stream.h
index 9e0c08e3..7732e5e5 100644
--- a/libsoup/soup-body-input-stream.h
+++ b/libsoup/soup-body-input-stream.h
@@ -39,9 +39,9 @@ typedef struct {
GType soup_body_input_stream_get_type (void);
-GInputStream *soup_body_input_stream_new (SoupFilterInputStream *base_stream,
- SoupEncoding encoding,
- goffset content_length);
+GInputStream *soup_body_input_stream_new (GInputStream *base_stream,
+ SoupEncoding encoding,
+ goffset content_length);
G_END_DECLS
diff --git a/libsoup/soup-body-output-stream.c b/libsoup/soup-body-output-stream.c
index 86f131ab..03353ef5 100644
--- a/libsoup/soup-body-output-stream.c
+++ b/libsoup/soup-body-output-stream.c
@@ -104,6 +104,7 @@ static gssize
soup_body_output_stream_write_raw (SoupBodyOutputStream *bostream,
const void *buffer,
gsize count,
+ gboolean blocking,
GCancellable *cancellable,
GError **error)
{
@@ -122,9 +123,9 @@ soup_body_output_stream_write_raw (SoupBodyOutputStream *bostream,
} else
my_count = count;
- nwrote = g_output_stream_write (bostream->priv->base_stream,
- buffer, my_count,
- cancellable, error);
+ nwrote = g_pollable_stream_write (bostream->priv->base_stream,
+ buffer, my_count,
+ blocking, cancellable, error);
if (nwrote > 0 && bostream->priv->write_length)
bostream->priv->written += nwrote;
@@ -139,6 +140,7 @@ static gssize
soup_body_output_stream_write_chunked (SoupBodyOutputStream *bostream,
const void *buffer,
gsize count,
+ gboolean blocking,
GCancellable *cancellable,
GError **error)
{
@@ -148,8 +150,9 @@ soup_body_output_stream_write_chunked (SoupBodyOutputStream *bostream,
again:
len = strlen (buf);
if (len) {
- nwrote = g_output_stream_write (bostream->priv->base_stream,
- buf, len, cancellable, error);
+ nwrote = g_pollable_stream_write (bostream->priv->base_stream,
+ buf, len, blocking,
+ cancellable, error);
if (nwrote < 0)
return nwrote;
memmove (buf, buf + nwrote, len + 1 - nwrote);
@@ -169,8 +172,9 @@ again:
break;
case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK:
- nwrote = g_output_stream_write (bostream->priv->base_stream,
- buffer, count, cancellable, error);
+ nwrote = g_pollable_stream_write (bostream->priv->base_stream,
+ buffer, count, blocking,
+ cancellable, error);
if (nwrote < (gssize)count)
return nwrote;
@@ -212,11 +216,11 @@ soup_body_output_stream_write_fn (GOutputStream *stream,
switch (bostream->priv->encoding) {
case SOUP_ENCODING_CHUNKED:
return soup_body_output_stream_write_chunked (bostream, buffer, count,
- cancellable, error);
+ TRUE, cancellable, error);
default:
return soup_body_output_stream_write_raw (bostream, buffer, count,
- cancellable, error);
+ TRUE, cancellable, error);
}
}
@@ -227,8 +231,9 @@ soup_body_output_stream_close_fn (GOutputStream *stream,
{
SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
- if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED) {
- if (soup_body_output_stream_write_chunked (bostream, NULL, 0, cancellable, error) == -1)
+ if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED &&
+ bostream->priv->chunked_state == SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE) {
+ if (soup_body_output_stream_write_chunked (bostream, NULL, 0, TRUE, cancellable, error) == -1)
return FALSE;
}
@@ -244,6 +249,28 @@ soup_body_output_stream_is_writable (GPollableOutputStream *stream)
g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream));
}
+static gssize
+soup_body_output_stream_write_nonblocking (GPollableOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GError **error)
+{
+ SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
+
+ if (bostream->priv->eof)
+ return count;
+
+ switch (bostream->priv->encoding) {
+ case SOUP_ENCODING_CHUNKED:
+ return soup_body_output_stream_write_chunked (bostream, buffer, count,
+ FALSE, NULL, error);
+
+ default:
+ return soup_body_output_stream_write_raw (bostream, buffer, count,
+ FALSE, NULL, error);
+ }
+}
+
static GSource *
soup_body_output_stream_create_source (GPollableOutputStream *stream,
GCancellable *cancellable)
@@ -301,6 +328,7 @@ soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_
gpointer interface_data)
{
pollable_interface->is_writable = soup_body_output_stream_is_writable;
+ pollable_interface->write_nonblocking = soup_body_output_stream_write_nonblocking;
pollable_interface->create_source = soup_body_output_stream_create_source;
}
diff --git a/libsoup/soup-cache-input-stream.c b/libsoup/soup-cache-input-stream.c
new file mode 100644
index 00000000..679531d0
--- /dev/null
+++ b/libsoup/soup-cache-input-stream.c
@@ -0,0 +1,351 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012 Igalia, S.L.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include "soup-cache-input-stream.h"
+#include "soup-message-body.h"
+
+static void soup_cache_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (SoupCacheInputStream, soup_cache_input_stream, SOUP_TYPE_FILTER_INPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
+ soup_cache_input_stream_pollable_init))
+
+/* properties */
+enum {
+ PROP_0,
+
+ PROP_OUTPUT_STREAM,
+
+ LAST_PROP
+};
+
+enum {
+ CACHING_FINISHED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _SoupCacheInputStreamPrivate
+{
+ GOutputStream *output_stream;
+ GCancellable *cancellable;
+ gsize bytes_written;
+
+ gboolean read_finished;
+ SoupBuffer *current_writing_buffer;
+ GQueue *buffer_queue;
+};
+
+static void soup_cache_input_stream_write_next_buffer (SoupCacheInputStream *istream);
+
+static inline void
+notify_and_clear (SoupCacheInputStream *istream, GError *error)
+{
+ SoupCacheInputStreamPrivate *priv = istream->priv;
+
+ g_signal_emit (istream, signals[CACHING_FINISHED], 0, priv->bytes_written, error);
+
+ g_clear_object (&priv->cancellable);
+ g_clear_object (&priv->output_stream);
+}
+
+static inline void
+try_write_next_buffer (SoupCacheInputStream *istream)
+{
+ SoupCacheInputStreamPrivate *priv = istream->priv;
+
+ if (priv->current_writing_buffer == NULL && priv->buffer_queue->length)
+ soup_cache_input_stream_write_next_buffer (istream);
+ else if (priv->read_finished)
+ notify_and_clear (istream, NULL);
+ else if (g_input_stream_is_closed (G_INPUT_STREAM (istream))) {
+ GError *error = NULL;
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+ _("Network stream unexpectedly closed"));
+ notify_and_clear (istream, error);
+ }
+}
+
+static void
+file_replaced_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ SoupCacheInputStream *istream = SOUP_CACHE_INPUT_STREAM (user_data);
+ SoupCacheInputStreamPrivate *priv = istream->priv;
+ GError *error = NULL;
+
+ priv->output_stream = (GOutputStream *) g_file_replace_finish (G_FILE (source), res, &error);
+
+ if (error)
+ notify_and_clear (istream, error);
+ else
+ try_write_next_buffer (istream);
+
+ g_object_unref (istream);
+}
+
+static void
+soup_cache_input_stream_init (SoupCacheInputStream *self)
+{
+ SoupCacheInputStreamPrivate *priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (self, SOUP_TYPE_CACHE_INPUT_STREAM,
+ SoupCacheInputStreamPrivate);
+
+ priv->buffer_queue = g_queue_new ();
+ self->priv = priv;
+}
+
+static void
+soup_cache_input_stream_get_property (GObject *object,
+ guint property_id, GValue *value, GParamSpec *pspec)
+{
+ SoupCacheInputStream *self = SOUP_CACHE_INPUT_STREAM (object);
+ SoupCacheInputStreamPrivate *priv = self->priv;
+
+ switch (property_id) {
+ case PROP_OUTPUT_STREAM:
+ g_value_set_object (value, priv->output_stream);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_cache_input_stream_set_property (GObject *object,
+ guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ SoupCacheInputStream *self = SOUP_CACHE_INPUT_STREAM (object);
+ SoupCacheInputStreamPrivate *priv = self->priv;
+
+ switch (property_id) {
+ case PROP_OUTPUT_STREAM:
+ priv->output_stream = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_cache_input_stream_finalize (GObject *object)
+{
+ SoupCacheInputStream *self = (SoupCacheInputStream *)object;
+ SoupCacheInputStreamPrivate *priv = self->priv;
+
+ g_clear_object (&priv->cancellable);
+ g_clear_object (&priv->output_stream);
+ g_clear_pointer (&priv->current_writing_buffer, soup_buffer_free);
+ g_queue_free_full (priv->buffer_queue, (GDestroyNotify) soup_buffer_free);
+
+ G_OBJECT_CLASS (soup_cache_input_stream_parent_class)->finalize (object);
+}
+
+static void
+write_ready_cb (GObject *source, GAsyncResult *result, SoupCacheInputStream *istream)
+{
+ GOutputStream *ostream = G_OUTPUT_STREAM (source);
+ SoupCacheInputStreamPrivate *priv = istream->priv;
+ gssize write_size;
+ gsize pending;
+ GError *error = NULL;
+
+ write_size = g_output_stream_write_finish (ostream, result, &error);
+ if (error) {
+ notify_and_clear (istream, error);
+ g_object_unref (istream);
+ return;
+ }
+
+ /* Check that we have written everything */
+ pending = priv->current_writing_buffer->length - write_size;
+ if (pending) {
+ SoupBuffer *subbuffer = soup_buffer_new_subbuffer (priv->current_writing_buffer,
+ write_size, pending);
+ g_queue_push_head (priv->buffer_queue, subbuffer);
+ }
+
+ priv->bytes_written += write_size;
+ g_clear_pointer (&priv->current_writing_buffer, soup_buffer_free);
+
+ try_write_next_buffer (istream);
+ g_object_unref (istream);
+}
+
+static void
+soup_cache_input_stream_write_next_buffer (SoupCacheInputStream *istream)
+{
+ SoupCacheInputStreamPrivate *priv = istream->priv;
+ SoupBuffer *buffer = g_queue_pop_head (priv->buffer_queue);
+ int priority;
+
+ g_assert (priv->output_stream && !g_output_stream_is_closed (priv->output_stream));
+
+ g_clear_pointer (&priv->current_writing_buffer, soup_buffer_free);
+ priv->current_writing_buffer = buffer;
+
+ if (priv->buffer_queue->length > 10)
+ priority = G_PRIORITY_DEFAULT;
+ else
+ priority = G_PRIORITY_LOW;
+
+ g_output_stream_write_async (priv->output_stream, buffer->data, buffer->length,
+ priority, priv->cancellable,
+ (GAsyncReadyCallback) write_ready_cb,
+ g_object_ref (istream));
+}
+
+static gssize
+read_internal (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupCacheInputStream *istream = SOUP_CACHE_INPUT_STREAM (stream);
+ SoupCacheInputStreamPrivate *priv = istream->priv;
+ GInputStream *base_stream;
+ gssize nread;
+
+ base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (stream));
+ nread = g_pollable_stream_read (base_stream, buffer, count, blocking,
+ cancellable, error);
+
+ if (G_UNLIKELY (nread == -1 || priv->read_finished))
+ return nread;
+
+ if (nread == 0) {
+ priv->read_finished = TRUE;
+
+ if (priv->current_writing_buffer == NULL && priv->output_stream)
+ notify_and_clear (istream, NULL);
+ } else {
+ SoupBuffer *soup_buffer = soup_buffer_new (SOUP_MEMORY_COPY, buffer, nread);
+ g_queue_push_tail (priv->buffer_queue, soup_buffer);
+
+ if (priv->current_writing_buffer == NULL && priv->output_stream)
+ soup_cache_input_stream_write_next_buffer (istream);
+ }
+
+ return nread;
+}
+
+static gssize
+soup_cache_input_stream_read_fn (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return read_internal (stream, buffer, count, TRUE,
+ cancellable, error);
+}
+
+static gssize
+soup_cache_input_stream_read_nonblocking (GPollableInputStream *stream,
+ void *buffer,
+ gsize count,
+ GError **error)
+{
+ return read_internal (G_INPUT_STREAM (stream), buffer, count, FALSE,
+ NULL, error);
+}
+
+static void
+soup_cache_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
+ gpointer interface_data)
+{
+ pollable_interface->read_nonblocking = soup_cache_input_stream_read_nonblocking;
+}
+
+static gboolean
+soup_cache_input_stream_close_fn (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupCacheInputStream *istream = SOUP_CACHE_INPUT_STREAM (stream);
+ SoupCacheInputStreamPrivate *priv = istream->priv;
+
+ if (!priv->read_finished) {
+ if (priv->output_stream) {
+ /* Cancel any pending write operation or return an error if none. */
+ if (g_output_stream_has_pending (priv->output_stream))
+ g_cancellable_cancel (priv->cancellable);
+ else {
+ GError *error = NULL;
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
+ _("Failed to completely cache the resource"));
+ notify_and_clear (istream, error);
+ }
+ } else if (priv->cancellable)
+ /* The file_replace_async() hasn't finished yet */
+ g_cancellable_cancel (priv->cancellable);
+ }
+
+ return G_INPUT_STREAM_CLASS (soup_cache_input_stream_parent_class)->close_fn (stream, cancellable, error);
+}
+
+static void
+soup_cache_input_stream_class_init (SoupCacheInputStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (SoupCacheInputStreamPrivate));
+
+ gobject_class->get_property = soup_cache_input_stream_get_property;
+ gobject_class->set_property = soup_cache_input_stream_set_property;
+ gobject_class->finalize = soup_cache_input_stream_finalize;
+
+ istream_class->read_fn = soup_cache_input_stream_read_fn;
+ istream_class->close_fn = soup_cache_input_stream_close_fn;
+
+ g_object_class_install_property (gobject_class, PROP_OUTPUT_STREAM,
+ g_param_spec_object ("output-stream", "Output stream",
+ "the output stream where to write.",
+ G_TYPE_OUTPUT_STREAM,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ signals[CACHING_FINISHED] =
+ g_signal_new ("caching-finished",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (SoupCacheInputStreamClass, caching_finished),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT, G_TYPE_ERROR);
+}
+
+GInputStream *
+soup_cache_input_stream_new (GInputStream *base_stream,
+ GFile *file)
+{
+ SoupCacheInputStream *istream = g_object_new (SOUP_TYPE_CACHE_INPUT_STREAM,
+ "base-stream", base_stream,
+ "close-base-stream", FALSE,
+ NULL);
+
+ istream->priv->cancellable = g_cancellable_new ();
+ g_file_replace_async (file, NULL, FALSE,
+ G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION,
+ G_PRIORITY_DEFAULT, istream->priv->cancellable,
+ file_replaced_cb, g_object_ref (istream));
+
+ return (GInputStream *) istream;
+}
diff --git a/libsoup/soup-cache-input-stream.h b/libsoup/soup-cache-input-stream.h
new file mode 100644
index 00000000..92b1d7b6
--- /dev/null
+++ b/libsoup/soup-cache-input-stream.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cache-input-stream.h - Header for SoupCacheInputStream
+ */
+
+#ifndef __SOUP_CACHE_INPUT_STREAM_H__
+#define __SOUP_CACHE_INPUT_STREAM_H__
+
+#include "soup-filter-input-stream.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CACHE_INPUT_STREAM (soup_cache_input_stream_get_type())
+#define SOUP_CACHE_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CACHE_INPUT_STREAM, SoupCacheInputStream))
+#define SOUP_CACHE_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CACHE_INPUT_STREAM, SoupCacheInputStreamClass))
+#define SOUP_IS_CACHE_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CACHE_INPUT_STREAM))
+#define SOUP_IS_CACHE_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_CACHE_INPUT_STREAM))
+#define SOUP_CACHE_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CACHE_INPUT_STREAM, SoupCacheInputStreamClass))
+
+typedef struct _SoupCacheInputStream SoupCacheInputStream;
+typedef struct _SoupCacheInputStreamClass SoupCacheInputStreamClass;
+typedef struct _SoupCacheInputStreamPrivate SoupCacheInputStreamPrivate;
+
+struct _SoupCacheInputStreamClass
+{
+ SoupFilterInputStreamClass parent_class;
+
+ /* signals */
+ void (*caching_finished) (SoupCacheInputStream *istream, gsize bytes_written, GError *error);
+};
+
+struct _SoupCacheInputStream
+{
+ SoupFilterInputStream parent;
+
+ SoupCacheInputStreamPrivate *priv;
+};
+
+GType soup_cache_input_stream_get_type (void) G_GNUC_CONST;
+
+GInputStream *soup_cache_input_stream_new (GInputStream *base_stream,
+ GFile *file);
+
+G_END_DECLS
+
+#endif /* __SOUP_CACHE_INPUT_STREAM_H__ */
diff --git a/libsoup/soup-cache-private.h b/libsoup/soup-cache-private.h
index 3843e8e9..e17fc0de 100644
--- a/libsoup/soup-cache-private.h
+++ b/libsoup/soup-cache-private.h
@@ -28,14 +28,18 @@
G_BEGIN_DECLS
-SoupCacheResponse soup_cache_has_response (SoupCache *cache,
- SoupMessage *msg);
-GInputStream *soup_cache_send_response (SoupCache *cache,
- SoupMessage *msg);
-SoupCacheability soup_cache_get_cacheability (SoupCache *cache,
- SoupMessage *msg);
-SoupMessage *soup_cache_generate_conditional_request (SoupCache *cache,
- SoupMessage *original);
+SoupCacheResponse soup_cache_has_response (SoupCache *cache,
+ SoupMessage *msg);
+GInputStream *soup_cache_send_response (SoupCache *cache,
+ SoupMessage *msg);
+SoupCacheability soup_cache_get_cacheability (SoupCache *cache,
+ SoupMessage *msg);
+SoupMessage *soup_cache_generate_conditional_request (SoupCache *cache,
+ SoupMessage *original);
+void soup_cache_cancel_conditional_request (SoupCache *cache,
+ SoupMessage *msg);
+void soup_cache_update_from_conditional_request (SoupCache *cache,
+ SoupMessage *msg);
G_END_DECLS
diff --git a/libsoup/soup-cache.c b/libsoup/soup-cache.c
index 8b26478d..c17e537c 100644
--- a/libsoup/soup-cache.c
+++ b/libsoup/soup-cache.c
@@ -31,15 +31,28 @@
#include <string.h>
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include "soup-cache.h"
+#include "soup-body-input-stream.h"
+#include "soup-cache-input-stream.h"
#include "soup-cache-private.h"
+#include "soup-content-processor.h"
+#include "soup-message-private.h"
#include "soup.h"
+#include "soup-message-private.h"
+
+/**
+ * SECTION:soup-cache
+ * @short_description: Caching support
+ *
+ * #SoupCache implements a file-based cache for HTTP resources.
+ */
static SoupSessionFeatureInterface *soup_cache_default_feature_interface;
static void soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupContentProcessorInterface *soup_cache_default_content_processor_interface;
+static void soup_cache_content_processor_init (SoupContentProcessorInterface *interface, gpointer interface_data);
+
#define DEFAULT_MAX_SIZE 50 * 1024 * 1024
#define MAX_ENTRY_DATA_PERCENTAGE 10 /* Percentage of the total size
of the cache that can be
@@ -65,6 +78,19 @@ static void soup_cache_session_feature_init (SoupSessionFeatureInterface *featur
*/
#define SOUP_CACHE_CURRENT_VERSION 5
+#define OLD_SOUP_CACHE_FILE "soup.cache"
+#define SOUP_CACHE_FILE "soup.cache2"
+
+#define SOUP_CACHE_HEADERS_FORMAT "{ss}"
+#define SOUP_CACHE_PHEADERS_FORMAT "(sbuuuuuqa" SOUP_CACHE_HEADERS_FORMAT ")"
+#define SOUP_CACHE_ENTRIES_FORMAT "(qa" SOUP_CACHE_PHEADERS_FORMAT ")"
+
+/* Basically the same format than above except that some strings are
+ prepended with &. This way the GVariant returns a pointer to the
+ data instead of duplicating the string */
+#define SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}"
+
+
typedef struct _SoupCacheEntry {
guint32 key;
char *uri;
@@ -73,13 +99,9 @@ typedef struct _SoupCacheEntry {
gsize length;
guint32 corrected_initial_age;
guint32 response_time;
- SoupBuffer *current_writing_buffer;
gboolean dirty;
- gboolean got_body;
gboolean being_validated;
SoupMessageHeaders *headers;
- GOutputStream *stream;
- GError *error;
guint32 hits;
GCancellable *cancellable;
guint16 status_code;
@@ -97,17 +119,6 @@ struct _SoupCachePrivate {
GList *lru_start;
};
-typedef struct {
- SoupCache *cache;
- SoupCacheEntry *entry;
- SoupMessage *msg;
- gulong content_sniffed_handler;
- gulong got_chunk_handler;
- gulong got_body_handler;
- gulong restarted_handler;
- GQueue *buffer_queue;
-} SoupCacheWritingFixture;
-
enum {
PROP_0,
PROP_CACHE_DIR,
@@ -118,12 +129,13 @@ enum {
G_DEFINE_TYPE_WITH_CODE (SoupCache, soup_cache, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
- soup_cache_session_feature_init))
+ soup_cache_session_feature_init)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_CONTENT_PROCESSOR,
+ soup_cache_content_processor_init))
-static gboolean soup_cache_entry_remove (SoupCache *cache, SoupCacheEntry *entry);
+static gboolean soup_cache_entry_remove (SoupCache *cache, SoupCacheEntry *entry, gboolean purge);
static void make_room_for_new_entry (SoupCache *cache, guint length_to_add);
static gboolean cache_accepts_entries_of_size (SoupCache *cache, guint length_to_add);
-static gboolean write_next_buffer (SoupCacheEntry *entry, SoupCacheWritingFixture *fixture);
static GFile *
get_file_from_entry (SoupCache *cache, SoupCacheEntry *entry)
@@ -260,17 +272,10 @@ get_cacheability (SoupCache *cache, SoupMessage *msg)
* and also unref's the GFile object representing it.
*/
static void
-soup_cache_entry_free (SoupCacheEntry *entry, GFile *file)
+soup_cache_entry_free (SoupCacheEntry *entry)
{
- if (file) {
- g_file_delete (file, NULL, NULL);
- g_object_unref (file);
- }
-
g_free (entry->uri);
- g_clear_pointer (&entry->current_writing_buffer, soup_buffer_free);
g_clear_pointer (&entry->headers, soup_message_headers_free);
- g_clear_error (&entry->error);
g_clear_object (&entry->cancellable);
g_slice_free (SoupCacheEntry, entry);
@@ -282,6 +287,12 @@ copy_headers (const char *name, const char *value, SoupMessageHeaders *headers)
soup_message_headers_append (headers, name, value);
}
+static void
+remove_headers (const char *name, const char *value, SoupMessageHeaders *headers)
+{
+ soup_message_headers_remove (headers, name);
+}
+
static char *hop_by_hop_headers[] = {"Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailer", "Transfer-Encoding", "Upgrade"};
static void
@@ -324,8 +335,14 @@ soup_cache_entry_set_freshness (SoupCacheEntry *entry, SoupMessage *msg, SoupCac
const char *cache_control;
const char *expires, *date, *last_modified;
+ /* Reset these values. We have to do this to ensure that
+ * revalidations overwrite previous values for the headers.
+ */
+ entry->must_revalidate = FALSE;
+ entry->freshness_lifetime = 0;
+
cache_control = soup_message_headers_get_list (entry->headers, "Cache-Control");
- if (cache_control) {
+ if (cache_control && *cache_control) {
const char *max_age, *s_maxage;
gint64 freshness_lifetime = 0;
GHashTable *hash;
@@ -442,10 +459,7 @@ soup_cache_entry_new (SoupCache *cache, SoupMessage *msg, time_t request_time, t
entry = g_slice_new0 (SoupCacheEntry);
entry->dirty = FALSE;
- entry->current_writing_buffer = NULL;
- entry->got_body = FALSE;
entry->being_validated = FALSE;
- entry->error = NULL;
entry->status_code = msg->status_code;
entry->response_time = response_time;
entry->uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
@@ -488,243 +502,11 @@ soup_cache_entry_new (SoupCache *cache, SoupMessage *msg, time_t request_time, t
return entry;
}
-static void
-soup_cache_writing_fixture_free (SoupCacheWritingFixture *fixture)
-{
- /* Free fixture. And disconnect signals, we don't want to
- listen to more SoupMessage events as we're finished with
- this resource */
- if (g_signal_handler_is_connected (fixture->msg, fixture->content_sniffed_handler))
- g_signal_handler_disconnect (fixture->msg, fixture->content_sniffed_handler);
- if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler))
- g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler);
- if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler))
- g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler);
- if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler))
- g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler);
- g_queue_foreach (fixture->buffer_queue, (GFunc) soup_buffer_free, NULL);
- g_queue_free (fixture->buffer_queue);
- g_object_unref (fixture->msg);
- g_object_unref (fixture->cache);
- g_slice_free (SoupCacheWritingFixture, fixture);
-}
-
-static void
-msg_content_sniffed_cb (SoupMessage *msg, gchar *content_type, GHashTable *params, SoupCacheWritingFixture *fixture)
-{
- soup_message_headers_set_content_type (fixture->entry->headers, content_type, params);
-}
-
-static void
-close_ready_cb (GObject *source, GAsyncResult *result, SoupCacheWritingFixture *fixture)
-{
- SoupCacheEntry *entry = fixture->entry;
- SoupCache *cache = fixture->cache;
- GOutputStream *stream = G_OUTPUT_STREAM (source);
- goffset content_length;
-
- g_warn_if_fail (entry->error == NULL);
-
- /* FIXME: what do we do on error ? */
-
- if (stream) {
- g_output_stream_close_finish (stream, result, NULL);
- g_object_unref (stream);
- }
- entry->stream = NULL;
-
- content_length = soup_message_headers_get_content_length (entry->headers);
-
- /* If the process was cancelled, then delete the entry from
- the cache. Do it also if the size of a chunked resource is
- too much for the cache */
- if (g_cancellable_is_cancelled (entry->cancellable)) {
- entry->dirty = FALSE;
- soup_cache_entry_remove (cache, entry);
- soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
- entry = NULL;
- } else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) ||
- entry->length != (gsize) content_length) {
- /* Two options here:
- *
- * 1. "chunked" data, entry was temporarily added to
- * cache (as content-length is 0) and now that we have
- * the actual size we have to evaluate if we want it
- * in the cache or not
- *
- * 2. Content-Length has a different value than actual
- * length, means that the content was encoded for
- * transmission (typically compressed) and thus we
- * have to substract the content-length value that was
- * added to the cache and add the unencoded length
- */
- gint length_to_add = entry->length - content_length;
-
- /* Make room in cache if needed */
- if (cache_accepts_entries_of_size (cache, length_to_add)) {
- make_room_for_new_entry (cache, length_to_add);
-
- cache->priv->size += length_to_add;
- } else {
- entry->dirty = FALSE;
- soup_cache_entry_remove (cache, entry);
- soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
- entry = NULL;
- }
- }
-
- if (entry) {
- entry->dirty = FALSE;
- entry->got_body = FALSE;
-
- if (entry->current_writing_buffer) {
- soup_buffer_free (entry->current_writing_buffer);
- entry->current_writing_buffer = NULL;
- }
-
- g_object_unref (entry->cancellable);
- entry->cancellable = NULL;
- }
-
- cache->priv->n_pending--;
-
- /* Frees */
- soup_cache_writing_fixture_free (fixture);
-}
-
-static void
-write_ready_cb (GObject *source, GAsyncResult *result, SoupCacheWritingFixture *fixture)
-{
- GOutputStream *stream = G_OUTPUT_STREAM (source);
- GError *error = NULL;
- gssize write_size;
- SoupCacheEntry *entry = fixture->entry;
-
- if (g_cancellable_is_cancelled (entry->cancellable)) {
- g_output_stream_close_async (stream,
- G_PRIORITY_LOW,
- entry->cancellable,
- (GAsyncReadyCallback)close_ready_cb,
- fixture);
- return;
- }
-
- write_size = g_output_stream_write_finish (stream, result, &error);
- if (write_size <= 0 || error) {
- if (error)
- entry->error = error;
- g_output_stream_close_async (stream,
- G_PRIORITY_LOW,
- entry->cancellable,
- (GAsyncReadyCallback)close_ready_cb,
- fixture);
- /* FIXME: We should completely stop caching the
- resource at this point */
- } else {
- /* Are we still writing and is there new data to write
- already ? */
- if (fixture->buffer_queue->length > 0)
- write_next_buffer (entry, fixture);
- else {
- soup_buffer_free (entry->current_writing_buffer);
- entry->current_writing_buffer = NULL;
-
- if (entry->got_body) {
- /* If we already received 'got-body'
- and we have written all the data,
- we can close the stream */
- g_output_stream_close_async (entry->stream,
- G_PRIORITY_LOW,
- entry->cancellable,
- (GAsyncReadyCallback)close_ready_cb,
- fixture);
- }
- }
- }
-}
-
static gboolean
-write_next_buffer (SoupCacheEntry *entry, SoupCacheWritingFixture *fixture)
-{
- SoupBuffer *buffer = g_queue_pop_head (fixture->buffer_queue);
-
- if (buffer == NULL)
- return FALSE;
-
- /* Free the old buffer */
- if (entry->current_writing_buffer) {
- soup_buffer_free (entry->current_writing_buffer);
- entry->current_writing_buffer = NULL;
- }
- entry->current_writing_buffer = buffer;
-
- g_output_stream_write_async (entry->stream, buffer->data, buffer->length,
- G_PRIORITY_LOW, entry->cancellable,
- (GAsyncReadyCallback) write_ready_cb,
- fixture);
- return TRUE;
-}
-
-static void
-msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, SoupCacheWritingFixture *fixture)
-{
- SoupCacheEntry *entry = fixture->entry;
-
- /* Ignore this if the writing or appending was cancelled */
- if (!g_cancellable_is_cancelled (entry->cancellable)) {
- g_queue_push_tail (fixture->buffer_queue, soup_buffer_copy (chunk));
- entry->length += chunk->length;
-
- if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) {
- /* Quickly cancel the caching of the resource */
- g_cancellable_cancel (entry->cancellable);
- }
- }
-
- /* FIXME: remove the error check when we cancel the caching at
- the first write error */
- /* Only write if the entry stream is ready */
- if (entry->current_writing_buffer == NULL && entry->error == NULL && entry->stream)
- write_next_buffer (entry, fixture);
-}
-
-static void
-msg_got_body_cb (SoupMessage *msg, SoupCacheWritingFixture *fixture)
-{
- SoupCacheEntry *entry = fixture->entry;
- g_return_if_fail (entry);
-
- entry->got_body = TRUE;
-
- if (!entry->stream && fixture->buffer_queue->length > 0)
- /* The stream is not ready to be written but we still
- have data to write, we'll write it when the stream
- is opened for writing */
- return;
-
-
- if (fixture->buffer_queue->length > 0) {
- /* If we still have data to write, write it,
- write_ready_cb will close the stream */
- if (entry->current_writing_buffer == NULL && entry->error == NULL && entry->stream)
- write_next_buffer (entry, fixture);
- return;
- }
-
- if (entry->stream && entry->current_writing_buffer == NULL)
- g_output_stream_close_async (entry->stream,
- G_PRIORITY_LOW,
- entry->cancellable,
- (GAsyncReadyCallback)close_ready_cb,
- fixture);
-}
-
-static gboolean
-soup_cache_entry_remove (SoupCache *cache, SoupCacheEntry *entry)
+soup_cache_entry_remove (SoupCache *cache, SoupCacheEntry *entry, gboolean purge)
{
GList *lru_item;
- /* if (entry->dirty && !g_cancellable_is_cancelled (entry->cancellable)) { */
if (entry->dirty) {
g_cancellable_cancel (entry->cancellable);
return FALSE;
@@ -745,6 +527,14 @@ soup_cache_entry_remove (SoupCache *cache, SoupCacheEntry *entry)
g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache));
+ /* Free resources */
+ if (purge) {
+ GFile *file = get_file_from_entry (cache, entry);
+ g_file_delete (file, NULL, NULL);
+ g_object_unref (file);
+ }
+ soup_cache_entry_free (entry);
+
return TRUE;
}
@@ -803,10 +593,9 @@ make_room_for_new_entry (SoupCache *cache, guint length_to_add)
/* Discard entries. Once cancelled resources will be
* freed in close_ready_cb
*/
- if (soup_cache_entry_remove (cache, old_entry)) {
- soup_cache_entry_free (old_entry, get_file_from_entry (cache, old_entry));
+ if (soup_cache_entry_remove (cache, old_entry, TRUE))
lru_entry = cache->priv->lru_start;
- } else
+ else
lru_entry = g_list_next (lru_entry);
}
}
@@ -822,7 +611,7 @@ soup_cache_entry_insert (SoupCache *cache,
/* Fill the key */
entry->key = get_cache_key_from_uri ((const char *) entry->uri);
- if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED)
+ if (soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CONTENT_LENGTH)
length_to_add = soup_message_headers_get_content_length (entry->headers);
/* Check if we are going to store the resource depending on its size */
@@ -836,9 +625,7 @@ soup_cache_entry_insert (SoupCache *cache,
/* Remove any previous entry */
if ((old_entry = g_hash_table_lookup (cache->priv->cache, GUINT_TO_POINTER (entry->key))) != NULL) {
- if (soup_cache_entry_remove (cache, old_entry))
- soup_cache_entry_free (old_entry, get_file_from_entry (cache, old_entry));
- else
+ if (!soup_cache_entry_remove (cache, old_entry, TRUE))
return FALSE;
}
@@ -879,157 +666,12 @@ soup_cache_entry_lookup (SoupCache *cache,
return entry;
}
-static void
-msg_restarted_cb (SoupMessage *msg, SoupCacheEntry *entry)
-{
- /* FIXME: What should we do here exactly? */
-}
-
-static void
-replace_cb (GObject *source, GAsyncResult *result, SoupCacheWritingFixture *fixture)
-{
- SoupCacheEntry *entry = fixture->entry;
- GOutputStream *stream = (GOutputStream *) g_file_replace_finish (G_FILE (source),
- result, &entry->error);
-
- if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) {
- if (stream)
- g_object_unref (stream);
- fixture->cache->priv->n_pending--;
- entry->dirty = FALSE;
- soup_cache_entry_remove (fixture->cache, entry);
- soup_cache_entry_free (entry, get_file_from_entry (fixture->cache, entry));
- soup_cache_writing_fixture_free (fixture);
- return;
- }
-
- entry->stream = stream;
-
- /* If we already got all the data we have to initiate the
- * writing here, since we won't get more 'got-chunk'
- * signals
- */
- if (!entry->got_body)
- return;
-
- /* It could happen that reading the data from server
- * was completed before this happens. In that case
- * there is no data
- */
- if (!write_next_buffer (entry, fixture))
- /* Could happen if the resource is empty */
- g_output_stream_close_async (stream, G_PRIORITY_LOW, entry->cancellable,
- (GAsyncReadyCallback) close_ready_cb,
- fixture);
-}
-
-typedef struct {
- time_t request_time;
- SoupSessionFeature *feature;
- gulong got_headers_handler;
-} RequestHelper;
-
-static void
-msg_got_headers_cb (SoupMessage *msg, gpointer user_data)
-{
- SoupCache *cache;
- SoupCacheability cacheable;
- RequestHelper *helper;
- time_t request_time, response_time;
- SoupCacheEntry *entry;
-
- response_time = time (NULL);
-
- helper = (RequestHelper *)user_data;
- cache = SOUP_CACHE (helper->feature);
- request_time = helper->request_time;
- g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data);
- g_slice_free (RequestHelper, helper);
-
- cacheable = soup_cache_get_cacheability (cache, msg);
-
- if (cacheable & SOUP_CACHE_CACHEABLE) {
- GFile *file;
- SoupCacheWritingFixture *fixture;
-
- /* Check if we are already caching this resource */
- entry = soup_cache_entry_lookup (cache, msg);
-
- if (entry && (entry->dirty || entry->being_validated))
- return;
-
- /* Create a new entry, deleting any old one if present */
- if (entry) {
- soup_cache_entry_remove (cache, entry);
- soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
- }
-
- entry = soup_cache_entry_new (cache, msg, request_time, response_time);
- entry->hits = 1;
-
- /* Do not continue if it can not be stored */
- if (!soup_cache_entry_insert (cache, entry, TRUE)) {
- soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
- return;
- }
-
- fixture = g_slice_new0 (SoupCacheWritingFixture);
- fixture->cache = g_object_ref (cache);
- fixture->entry = entry;
- fixture->msg = g_object_ref (msg);
- fixture->buffer_queue = g_queue_new ();
-
- /* We connect now to these signals and buffer the data
- if it comes before the file is ready for writing */
- fixture->content_sniffed_handler =
- g_signal_connect (msg, "content-sniffed", G_CALLBACK (msg_content_sniffed_cb), fixture);
- fixture->got_chunk_handler =
- g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture);
- fixture->got_body_handler =
- g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture);
- fixture->restarted_handler =
- g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry);
-
- /* Prepare entry */
- cache->priv->n_pending++;
-
- entry->dirty = TRUE;
- entry->cancellable = g_cancellable_new ();
- file = get_file_from_entry (cache, entry);
- g_file_replace_async (file, NULL, FALSE,
- G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION,
- G_PRIORITY_LOW, entry->cancellable,
- (GAsyncReadyCallback) replace_cb, fixture);
- g_object_unref (file);
- } else if (cacheable & SOUP_CACHE_INVALIDATES) {
- entry = soup_cache_entry_lookup (cache, msg);
-
- if (entry) {
- if (soup_cache_entry_remove (cache, entry))
- soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
- }
- } else if (cacheable & SOUP_CACHE_VALIDATES) {
- entry = soup_cache_entry_lookup (cache, msg);
-
- /* It's possible to get a CACHE_VALIDATES with no
- * entry in the hash table. This could happen if for
- * example the soup client is the one creating the
- * conditional request.
- */
- if (entry) {
- entry->being_validated = FALSE;
- copy_end_to_end_headers (msg->response_headers, entry->headers);
- soup_cache_entry_set_freshness (entry, msg, cache);
- }
- }
-}
-
GInputStream *
soup_cache_send_response (SoupCache *cache, SoupMessage *msg)
{
SoupCacheEntry *entry;
char *current_age;
- GInputStream *stream = NULL;
+ GInputStream *file_stream, *body_stream, *cache_stream;
GFile *file;
g_return_val_if_fail (SOUP_IS_CACHE (cache), NULL);
@@ -1038,18 +680,19 @@ soup_cache_send_response (SoupCache *cache, SoupMessage *msg)
entry = soup_cache_entry_lookup (cache, msg);
g_return_val_if_fail (entry, NULL);
- /* TODO: the original idea was to save reads, but current code
- assumes that a stream is always returned. Need to reach
- some agreement here. Also we have to handle the situation
- were the file was no longer there (for example files
- removed without notifying the cache */
file = get_file_from_entry (cache, entry);
- stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+ file_stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
g_object_unref (file);
/* Do not change the original message if there is no resource */
- if (stream == NULL)
- return stream;
+ if (!file_stream)
+ return NULL;
+
+ body_stream = soup_body_input_stream_new (file_stream, SOUP_ENCODING_CONTENT_LENGTH, entry->length);
+ g_object_unref (file_stream);
+
+ if (!body_stream)
+ return NULL;
/* If we are told to send a response from cache any validation
in course is over by now */
@@ -1068,19 +711,29 @@ soup_cache_send_response (SoupCache *cache, SoupMessage *msg)
current_age);
g_free (current_age);
- return stream;
+ /* Create the cache stream. */
+ soup_message_disable_feature (msg, SOUP_TYPE_CACHE);
+ cache_stream = soup_message_setup_body_istream (body_stream, msg,
+ cache->priv->session,
+ SOUP_STAGE_ENTITY_BODY);
+ g_object_unref (body_stream);
+
+ return cache_stream;
+}
+
+static void
+msg_got_headers_cb (SoupMessage *msg, gpointer user_data)
+{
+ g_object_set_data (G_OBJECT (msg), "response-time", GINT_TO_POINTER (time (NULL)));
+ g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data);
}
static void
request_started (SoupSessionFeature *feature, SoupSession *session,
SoupMessage *msg, SoupSocket *socket)
{
- RequestHelper *helper = g_slice_new0 (RequestHelper);
- helper->request_time = time (NULL);
- helper->feature = feature;
- helper->got_headers_handler = g_signal_connect (msg, "got-headers",
- G_CALLBACK (msg_got_headers_cb),
- helper);
+ g_object_set_data (G_OBJECT (msg), "request-time", GINT_TO_POINTER (time (NULL)));
+ g_signal_connect (msg, "got-headers", G_CALLBACK (msg_got_headers_cb), NULL);
}
static void
@@ -1103,6 +756,138 @@ soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface,
feature_interface->request_started = request_started;
}
+typedef struct {
+ SoupCache *cache;
+ SoupCacheEntry *entry;
+} StreamHelper;
+
+static void
+istream_caching_finished (SoupCacheInputStream *istream,
+ gsize bytes_written,
+ GError *error,
+ gpointer user_data)
+{
+ StreamHelper *helper = (StreamHelper *) user_data;
+ SoupCache *cache = helper->cache;
+ SoupCacheEntry *entry = helper->entry;
+
+ --cache->priv->n_pending;
+
+ entry->dirty = FALSE;
+ entry->length = bytes_written;
+ g_clear_object (&entry->cancellable);
+
+ if (error) {
+ /* Update cache size */
+ if (soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CONTENT_LENGTH)
+ cache->priv->size -= soup_message_headers_get_content_length (entry->headers);
+
+ soup_cache_entry_remove (cache, entry, TRUE);
+ helper->entry = entry = NULL;
+ goto cleanup;
+ }
+
+ if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CONTENT_LENGTH) {
+
+ if (cache_accepts_entries_of_size (cache, entry->length)) {
+ make_room_for_new_entry (cache, entry->length);
+ cache->priv->size += entry->length;
+ } else {
+ soup_cache_entry_remove (cache, entry, TRUE);
+ helper->entry = entry = NULL;
+ }
+ }
+
+ cleanup:
+ g_object_unref (helper->cache);
+ g_slice_free (StreamHelper, helper);
+}
+
+static GInputStream*
+soup_cache_content_processor_wrap_input (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error)
+{
+ SoupCache *cache = (SoupCache*) processor;
+ SoupCacheEntry *entry;
+ SoupCacheability cacheability;
+ GInputStream *istream;
+ GFile *file;
+ StreamHelper *helper;
+ time_t request_time, response_time;
+
+ /* First of all, check if we should cache the resource. */
+ cacheability = soup_cache_get_cacheability (cache, msg);
+ entry = soup_cache_entry_lookup (cache, msg);
+
+ if (cacheability & SOUP_CACHE_INVALIDATES) {
+ if (entry)
+ soup_cache_entry_remove (cache, entry, TRUE);
+ return NULL;
+ }
+
+ if (cacheability & SOUP_CACHE_VALIDATES) {
+ /* It's possible to get a CACHE_VALIDATES with no
+ * entry in the hash table. This could happen if for
+ * example the soup client is the one creating the
+ * conditional request.
+ */
+ if (entry)
+ soup_cache_update_from_conditional_request (cache, msg);
+ return NULL;
+ }
+
+ if (!(cacheability & SOUP_CACHE_CACHEABLE))
+ return NULL;
+
+ /* Check if we are already caching this resource */
+ if (entry && (entry->dirty || entry->being_validated))
+ return NULL;
+
+ /* Create a new entry, deleting any old one if present */
+ if (entry)
+ soup_cache_entry_remove (cache, entry, TRUE);
+
+ request_time = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "request-time"));
+ response_time = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "request-time"));
+ entry = soup_cache_entry_new (cache, msg, request_time, response_time);
+ entry->hits = 1;
+ entry->dirty = TRUE;
+
+ /* Do not continue if it can not be stored */
+ if (!soup_cache_entry_insert (cache, entry, TRUE)) {
+ soup_cache_entry_free (entry);
+ return NULL;
+ }
+
+ entry->cancellable = g_cancellable_new ();
+ ++cache->priv->n_pending;
+
+ helper = g_slice_new (StreamHelper);
+ helper->cache = g_object_ref (cache);
+ helper->entry = entry;
+
+ file = get_file_from_entry (cache, entry);
+ istream = soup_cache_input_stream_new (base_stream, file);
+ g_object_unref (file);
+
+ g_signal_connect (istream, "caching-finished", G_CALLBACK (istream_caching_finished), helper);
+
+ return istream;
+}
+
+static void
+soup_cache_content_processor_init (SoupContentProcessorInterface *processor_interface,
+ gpointer interface_data)
+{
+ soup_cache_default_content_processor_interface =
+ g_type_default_interface_peek (SOUP_TYPE_CONTENT_PROCESSOR);
+
+ processor_interface->processing_stage = SOUP_STAGE_ENTITY_BODY;
+ processor_interface->wrap_input = soup_cache_content_processor_wrap_input;
+}
+
static void
soup_cache_init (SoupCache *cache)
{
@@ -1127,11 +912,7 @@ static void
remove_cache_item (gpointer data,
gpointer user_data)
{
- SoupCache *cache = (SoupCache *) user_data;
- SoupCacheEntry *entry = (SoupCacheEntry *) data;
-
- if (soup_cache_entry_remove (cache, entry))
- soup_cache_entry_free (entry, NULL);
+ soup_cache_entry_remove ((SoupCache *) user_data, (SoupCacheEntry *) data, FALSE);
}
static void
@@ -1361,7 +1142,7 @@ soup_cache_has_response (SoupCache *cache, SoupMessage *msg)
return SOUP_CACHE_RESPONSE_STALE;
cache_control = soup_message_headers_get_list (msg->request_headers, "Cache-Control");
- if (cache_control) {
+ if (cache_control && *cache_control) {
GHashTable *hash = soup_header_parse_param_list (cache_control);
if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
@@ -1470,6 +1251,9 @@ force_flush_timeout (gpointer data)
* committed to disk. For doing so it will iterate the #GMainContext
* associated with @cache's session as long as needed.
*
+ * Contrast with soup_cache_dump(), which writes out the cache index
+ * file.
+ *
* Since: 2.34
*/
void
@@ -1502,19 +1286,39 @@ static void
clear_cache_item (gpointer data,
gpointer user_data)
{
- SoupCache *cache = (SoupCache *) user_data;
- SoupCacheEntry *entry = (SoupCacheEntry *) data;
+ soup_cache_entry_remove ((SoupCache *) user_data, (SoupCacheEntry *) data, TRUE);
+}
+
+static void
+clear_cache_files (SoupCache *cache)
+{
+ GFileInfo *file_info;
+ GFileEnumerator *file_enumerator;
+ GFile *cache_dir_file = g_file_new_for_path (cache->priv->cache_dir);
+
+ file_enumerator = g_file_enumerate_children (cache_dir_file, G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (file_enumerator) {
+ while ((file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL)) != NULL) {
+ const char *filename = g_file_info_get_name (file_info);
- if (soup_cache_entry_remove (cache, entry))
- soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ if (strcmp (filename, SOUP_CACHE_FILE) != 0) {
+ GFile *cache_file = g_file_get_child (cache_dir_file, filename);
+ g_file_delete (cache_file, NULL, NULL);
+ g_object_unref (cache_file);
+ }
+ g_object_unref (file_info);
+ }
+ g_object_unref (file_enumerator);
+ }
+ g_object_unref (cache_dir_file);
}
/**
* soup_cache_clear:
* @cache: a #SoupCache
*
- * Will remove all entries in the @cache plus all the cache files
- * associated with them.
+ * Will remove all entries in the @cache plus all the cache files.
*
* Since: 2.34
*/
@@ -1530,6 +1334,9 @@ soup_cache_clear (SoupCache *cache)
entries = g_hash_table_get_values (cache->priv->cache);
g_list_foreach (entries, clear_cache_item, cache);
g_list_free (entries);
+
+ /* Remove also any file not associated with a cache entry. */
+ clear_cache_files (cache);
}
SoupMessage *
@@ -1539,6 +1346,8 @@ soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original
SoupURI *uri;
SoupCacheEntry *entry;
const char *last_modified, *etag;
+ SoupMessagePrivate *origpriv;
+ GSList *f;
g_return_val_if_fail (SOUP_IS_CACHE (cache), NULL);
g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL);
@@ -1558,11 +1367,16 @@ soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original
/* Copy the data we need from the original message */
uri = soup_message_get_uri (original);
msg = soup_message_new_from_uri (original->method, uri);
+ soup_message_disable_feature (msg, SOUP_TYPE_CACHE);
soup_message_headers_foreach (original->request_headers,
(SoupMessageHeadersForeachFunc)copy_headers,
msg->request_headers);
+ origpriv = SOUP_MESSAGE_GET_PRIVATE (original);
+ for (f = origpriv->disabled_features; f; f = f->next)
+ soup_message_disable_feature (msg, (GType) GPOINTER_TO_SIZE (f->data));
+
if (last_modified)
soup_message_headers_append (msg->request_headers,
"If-Modified-Since",
@@ -1575,17 +1389,38 @@ soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original
return msg;
}
-#define OLD_SOUP_CACHE_FILE "soup.cache"
-#define SOUP_CACHE_FILE "soup.cache2"
+void
+soup_cache_cancel_conditional_request (SoupCache *cache,
+ SoupMessage *msg)
+{
+ SoupCacheEntry *entry;
-#define SOUP_CACHE_HEADERS_FORMAT "{ss}"
-#define SOUP_CACHE_PHEADERS_FORMAT "(sbuuuuuqa" SOUP_CACHE_HEADERS_FORMAT ")"
-#define SOUP_CACHE_ENTRIES_FORMAT "(qa" SOUP_CACHE_PHEADERS_FORMAT ")"
+ entry = soup_cache_entry_lookup (cache, msg);
+ if (entry)
+ entry->being_validated = FALSE;
-/* Basically the same format than above except that some strings are
- prepended with &. This way the GVariant returns a pointer to the
- data instead of duplicating the string */
-#define SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}"
+ soup_session_cancel_message (cache->priv->session, msg, SOUP_STATUS_CANCELLED);
+}
+
+void
+soup_cache_update_from_conditional_request (SoupCache *cache,
+ SoupMessage *msg)
+{
+ SoupCacheEntry *entry = soup_cache_entry_lookup (cache, msg);
+ if (!entry)
+ return;
+
+ entry->being_validated = FALSE;
+
+ if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
+ soup_message_headers_foreach (msg->response_headers,
+ (SoupMessageHeadersForeachFunc) remove_headers,
+ entry->headers);
+ copy_end_to_end_headers (msg->response_headers, entry->headers);
+
+ soup_cache_entry_set_freshness (entry, msg, cache);
+ }
+}
static void
pack_entry (gpointer data,
@@ -1597,7 +1432,7 @@ pack_entry (gpointer data,
GVariantBuilder *entries_builder = (GVariantBuilder *)user_data;
/* Do not store non-consolidated entries */
- if (entry->dirty || entry->current_writing_buffer != NULL || !entry->key)
+ if (entry->dirty || !entry->key)
return;
g_variant_builder_open (entries_builder, G_VARIANT_TYPE (SOUP_CACHE_PHEADERS_FORMAT));
@@ -1622,6 +1457,19 @@ pack_entry (gpointer data,
g_variant_builder_close (entries_builder); /* SOUP_CACHE_PHEADERS_FORMAT */
}
+/**
+ * soup_cache_dump:
+ * @cache: a #SoupCache
+ *
+ * Synchronously writes the cache index out to disk. Contrast with
+ * soup_cache_flush(), which writes pending cache
+ * <emphasis>entries</emphasis> to disk.
+ *
+ * You must call this before exiting if you want your cache data to
+ * persist between sessions.
+ *
+ * Since: 2.34.
+ */
void
soup_cache_dump (SoupCache *cache)
{
@@ -1650,30 +1498,14 @@ soup_cache_dump (SoupCache *cache)
g_variant_unref (cache_variant);
}
-static void
-clear_cache_files (SoupCache *cache)
-{
- GFileInfo *file_info;
- GFileEnumerator *file_enumerator;
- GFile *cache_dir_file = g_file_new_for_path (cache->priv->cache_dir);
-
- file_enumerator = g_file_enumerate_children (cache_dir_file, G_FILE_ATTRIBUTE_STANDARD_NAME,
- G_FILE_QUERY_INFO_NONE, NULL, NULL);
- if (file_enumerator) {
- while ((file_info = g_file_enumerator_next_file (file_enumerator, NULL, NULL)) != NULL) {
- const char *filename = g_file_info_get_name (file_info);
-
- if (strcmp (filename, SOUP_CACHE_FILE) != 0) {
- GFile *cache_file = g_file_get_child (cache_dir_file, filename);
- g_file_delete (cache_file, NULL, NULL);
- g_object_unref (cache_file);
- }
- }
- g_object_unref (file_enumerator);
- }
- g_object_unref (cache_dir_file);
-}
-
+/**
+ * soup_cache_load:
+ * @cache: a #SoupCache
+ *
+ * Loads the contents of @cache's index into memory.
+ *
+ * Since: 2.34
+ */
void
soup_cache_load (SoupCache *cache)
{
@@ -1741,7 +1573,7 @@ soup_cache_load (SoupCache *cache)
entry->status_code = status_code;
if (!soup_cache_entry_insert (cache, entry, FALSE))
- soup_cache_entry_free (entry, get_file_from_entry (cache, entry));
+ soup_cache_entry_free (entry);
}
cache->priv->lru_start = g_list_reverse (cache->priv->lru_start);
@@ -1751,6 +1583,15 @@ soup_cache_load (SoupCache *cache)
g_variant_unref (cache_variant);
}
+/**
+ * soup_cache_set_max_size:
+ * @cache: a #SoupCache
+ * @max_size: the maximum size of the cache, in bytes
+ *
+ * Sets the maximum size of the cache.
+ *
+ * Since: 2.34
+ */
void
soup_cache_set_max_size (SoupCache *cache,
guint max_size)
@@ -1759,6 +1600,16 @@ soup_cache_set_max_size (SoupCache *cache,
cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE;
}
+/**
+ * soup_cache_get_max_size:
+ * @cache: a #SoupCache
+ *
+ * Gets the maximum size of the cache.
+ *
+ * Return value: the maximum size of the cache, in bytes.
+ *
+ * Since: 2.34
+ */
guint
soup_cache_get_max_size (SoupCache *cache)
{
diff --git a/libsoup/soup-cache.h b/libsoup/soup-cache.h
index 8585d51f..ce19b5e1 100644
--- a/libsoup/soup-cache.h
+++ b/libsoup/soup-cache.h
@@ -23,8 +23,6 @@
#ifndef SOUP_CACHE_H
#define SOUP_CACHE_H 1
-#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include <libsoup/soup-types.h>
#include <gio/gio.h>
@@ -77,22 +75,28 @@ typedef struct {
void (*_libsoup_reserved3)(void);
} SoupCacheClass;
+SOUP_AVAILABLE_IN_2_34
GType soup_cache_get_type (void);
+SOUP_AVAILABLE_IN_2_34
SoupCache *soup_cache_new (const char *cache_dir,
SoupCacheType cache_type);
+SOUP_AVAILABLE_IN_2_34
void soup_cache_flush (SoupCache *cache);
+SOUP_AVAILABLE_IN_2_34
void soup_cache_clear (SoupCache *cache);
+SOUP_AVAILABLE_IN_2_34
void soup_cache_dump (SoupCache *cache);
+SOUP_AVAILABLE_IN_2_34
void soup_cache_load (SoupCache *cache);
+SOUP_AVAILABLE_IN_2_34
void soup_cache_set_max_size (SoupCache *cache,
guint max_size);
+SOUP_AVAILABLE_IN_2_34
guint soup_cache_get_max_size (SoupCache *cache);
G_END_DECLS
-#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
-
#endif /* SOUP_CACHE_H */
diff --git a/libsoup/soup-client-input-stream.c b/libsoup/soup-client-input-stream.c
index 5c18eaa0..0264cb79 100644
--- a/libsoup/soup-client-input-stream.c
+++ b/libsoup/soup-client-input-stream.c
@@ -11,7 +11,6 @@
#include "soup-client-input-stream.h"
#include "soup.h"
-#include "soup-marshal.h"
#include "soup-message-private.h"
struct _SoupClientInputStreamPrivate {
@@ -47,6 +46,16 @@ soup_client_input_stream_init (SoupClientInputStream *stream)
}
static void
+soup_client_input_stream_finalize (GObject *object)
+{
+ SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (object);
+
+ g_clear_object (&cistream->priv->msg);
+
+ G_OBJECT_CLASS (soup_client_input_stream_parent_class)->finalize (object);
+}
+
+static void
soup_client_input_stream_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
@@ -78,17 +87,6 @@ soup_client_input_stream_get_property (GObject *object, guint prop_id,
}
}
-/* Temporary HACK to keep SoupCache working. See soup_client_input_stream_read_fn()
- * and soup_client_input_stream_read_nonblocking().
- */
-static void
-soup_client_input_stream_emit_got_chunk (SoupClientInputStream *stream, void *data, gssize nread)
-{
- SoupBuffer *buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY, data, nread);
- soup_message_got_chunk (stream->priv->msg, buffer);
- soup_buffer_free (buffer);
-}
-
static gssize
soup_client_input_stream_read_fn (GInputStream *stream,
void *buffer,
@@ -104,12 +102,6 @@ soup_client_input_stream_read_fn (GInputStream *stream,
if (nread == 0)
g_signal_emit (stream, signals[EOF], 0);
- /* Temporary HACK to keep SoupCache working */
- if (nread > 0) {
- soup_client_input_stream_emit_got_chunk (SOUP_CLIENT_INPUT_STREAM (stream),
- buffer, nread);
- }
-
return nread;
}
@@ -127,12 +119,6 @@ soup_client_input_stream_read_nonblocking (GPollableInputStream *stream,
if (nread == 0)
g_signal_emit (stream, signals[EOF], 0);
- /* Temporary HACK to keep SoupCache working */
- if (nread > 0) {
- soup_client_input_stream_emit_got_chunk (SOUP_CLIENT_INPUT_STREAM (stream),
- buffer, nread);
- }
-
return nread;
}
@@ -142,45 +128,53 @@ soup_client_input_stream_close_fn (GInputStream *stream,
GError **error)
{
SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (stream);
+ gboolean success;
- return soup_message_io_run_until_finish (cistream->priv->msg,
- cancellable, error);
+ success = soup_message_io_run_until_finish (cistream->priv->msg, TRUE,
+ NULL, error);
+ soup_message_io_finished (cistream->priv->msg);
+ return success;
}
-typedef struct {
- SoupClientInputStream *cistream;
- gint priority;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
-} CloseAsyncData;
-
-static void
-close_async_data_free (CloseAsyncData *cad)
+static gboolean
+idle_finish_close (gpointer user_data)
{
- g_clear_object (&cad->cancellable);
- g_object_unref (cad->result);
- g_slice_free (CloseAsyncData, cad);
+ GTask *task = user_data;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return FALSE;
}
static gboolean
close_async_ready (SoupMessage *msg, gpointer user_data)
{
- CloseAsyncData *cad = user_data;
+ GTask *task = user_data;
+ SoupClientInputStream *cistream = g_task_get_source_object (task);
GError *error = NULL;
- if (!soup_message_io_run_until_finish (cad->cistream->priv->msg,
- cad->cancellable, &error) &&
+ if (!soup_message_io_run_until_finish (cistream->priv->msg, FALSE,
+ g_task_get_cancellable (task),
+ &error) &&
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
g_error_free (error);
return TRUE;
}
- if (error)
- g_simple_async_result_take_error (cad->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (cad->result, TRUE);
- g_simple_async_result_complete_in_idle (cad->result);
- close_async_data_free (cad);
+ soup_message_io_finished (cistream->priv->msg);
+
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return FALSE;
+ }
+
+ /* Due to a historical accident, SoupSessionAsync relies on us
+ * waiting one extra cycle after run_until_finish() returns.
+ * Ugh. FIXME later when it's easier to do.
+ */
+ soup_add_idle (g_main_context_get_thread_default (),
+ idle_finish_close, task);
return FALSE;
}
@@ -191,23 +185,20 @@ soup_client_input_stream_close_async (GInputStream *stream,
GAsyncReadyCallback callback,
gpointer user_data)
{
- CloseAsyncData *cad;
+ SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (stream);
+ GTask *task;
GSource *source;
- cad = g_slice_new (CloseAsyncData);
- cad->cistream = SOUP_CLIENT_INPUT_STREAM (stream);
- cad->result = g_simple_async_result_new (G_OBJECT (stream),
- callback, user_data,
- soup_client_input_stream_close_async);
- cad->priority = priority;
- cad->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-
- source = soup_message_io_get_source (cad->cistream->priv->msg,
- cancellable,
- close_async_ready, cad);
- g_source_set_priority (source, priority);
- g_source_attach (source, g_main_context_get_thread_default ());
- g_source_unref (source);
+ task = g_task_new (stream, cancellable, callback, user_data);
+ g_task_set_priority (task, priority);
+
+ if (close_async_ready (cistream->priv->msg, task) == G_SOURCE_CONTINUE) {
+ source = soup_message_io_get_source (cistream->priv->msg,
+ cancellable, NULL, NULL);
+
+ g_task_attach_source (task, source, (GSourceFunc) close_async_ready);
+ g_source_unref (source);
+ }
}
static gboolean
@@ -215,12 +206,7 @@ soup_client_input_stream_close_finish (GInputStream *stream,
GAsyncResult *result,
GError **error)
{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
-
- if (g_simple_async_result_propagate_error (simple, error))
- return FALSE;
- else
- return g_simple_async_result_get_op_res_gboolean (simple);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
@@ -231,6 +217,7 @@ soup_client_input_stream_class_init (SoupClientInputStreamClass *stream_class)
g_type_class_add_private (stream_class, sizeof (SoupClientInputStreamPrivate));
+ object_class->finalize = soup_client_input_stream_finalize;
object_class->set_property = soup_client_input_stream_set_property;
object_class->get_property = soup_client_input_stream_get_property;
@@ -245,7 +232,7 @@ soup_client_input_stream_class_init (SoupClientInputStreamClass *stream_class)
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
g_object_class_install_property (
diff --git a/libsoup/soup-connection-auth.c b/libsoup/soup-connection-auth.c
new file mode 100644
index 00000000..fc327335
--- /dev/null
+++ b/libsoup/soup-connection-auth.c
@@ -0,0 +1,173 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-connection-auth.c: Abstract base class for hacky Microsoft
+ * connection-based auth mechanisms (NTLM and Negotiate)
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <string.h>
+
+#include "soup-connection-auth.h"
+#include "soup.h"
+#include "soup-connection.h"
+#include "soup-message-private.h"
+
+G_DEFINE_ABSTRACT_TYPE (SoupConnectionAuth, soup_connection_auth, SOUP_TYPE_AUTH)
+
+struct SoupConnectionAuthPrivate {
+ GHashTable *conns;
+};
+
+static void
+soup_connection_auth_init (SoupConnectionAuth *auth)
+{
+ auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuthPrivate);
+
+ auth->priv->conns = g_hash_table_new (NULL, NULL);
+}
+
+static void connection_disconnected (SoupConnection *conn, gpointer user_data);
+
+static void
+soup_connection_auth_free_connection_state (SoupConnectionAuth *auth,
+ SoupConnection *conn,
+ gpointer state)
+{
+ g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_disconnected), auth);
+ SOUP_CONNECTION_AUTH_GET_CLASS (auth)->free_connection_state (auth, state);
+}
+
+static void
+connection_disconnected (SoupConnection *conn, gpointer user_data)
+{
+ SoupConnectionAuth *auth = user_data;
+ gpointer state;
+
+ state = g_hash_table_lookup (auth->priv->conns, conn);
+ g_hash_table_remove (auth->priv->conns, conn);
+ soup_connection_auth_free_connection_state (auth, conn, state);
+}
+
+static void
+soup_connection_auth_finalize (GObject *object)
+{
+ SoupConnectionAuth *auth = SOUP_CONNECTION_AUTH (object);
+ GHashTableIter iter;
+ gpointer conn, state;
+
+ g_hash_table_iter_init (&iter, auth->priv->conns);
+ while (g_hash_table_iter_next (&iter, &conn, &state)) {
+ soup_connection_auth_free_connection_state (auth, conn, state);
+ g_hash_table_iter_remove (&iter);
+ }
+ g_hash_table_destroy (auth->priv->conns);
+
+ G_OBJECT_CLASS (soup_connection_auth_parent_class)->finalize (object);
+}
+
+static gpointer
+get_connection_state_for_message (SoupConnectionAuth *auth, SoupMessage *msg)
+{
+ SoupConnection *conn;
+ gpointer state;
+
+ conn = soup_message_get_connection (msg);
+ state = g_hash_table_lookup (auth->priv->conns, conn);
+ if (state)
+ return state;
+
+ state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth);
+ if (conn) {
+ g_signal_connect (conn, "disconnected",
+ G_CALLBACK (connection_disconnected), auth);
+ }
+
+ g_hash_table_insert (auth->priv->conns, conn, state);
+ return state;
+}
+
+static gboolean
+soup_connection_auth_update (SoupAuth *auth,
+ SoupMessage *msg,
+ GHashTable *auth_params)
+{
+ SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
+ gpointer conn = get_connection_state_for_message (cauth, msg);
+ GHashTableIter iter;
+ GString *auth_header;
+ gpointer key, value;
+ gboolean result;
+
+ /* Recreate @auth_header out of @auth_params. If the
+ * base64 data ended with 1 or more "="s, then it
+ * will have been parsed as key=value. Otherwise
+ * it will all have been parsed as key, and value
+ * will be %NULL.
+ */
+ auth_header = g_string_new (soup_auth_get_scheme_name (auth));
+ g_hash_table_iter_init (&iter, auth_params);
+ if (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (value) {
+ g_string_append_printf (auth_header, " %s=%s",
+ (char *)key,
+ (char *)value);
+ } else {
+ g_string_append_printf (auth_header, " %s",
+ (char *)key);
+ }
+
+ if (g_hash_table_iter_next (&iter, &key, &value)) {
+ g_string_free (auth_header, TRUE);
+ return FALSE;
+ }
+ }
+
+ result = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
+ update_connection (cauth, msg, auth_header->str, conn);
+
+ g_string_free (auth_header, TRUE);
+ return result;
+}
+
+static char *
+soup_connection_auth_get_authorization (SoupAuth *auth,
+ SoupMessage *msg)
+{
+ SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
+ gpointer conn = get_connection_state_for_message (cauth, msg);
+
+ return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
+ get_connection_authorization (cauth, msg, conn);
+}
+
+static gboolean
+soup_connection_auth_is_ready (SoupAuth *auth,
+ SoupMessage *msg)
+{
+ SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
+ gpointer conn = get_connection_state_for_message (cauth, msg);
+
+ return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
+ is_connection_ready (SOUP_CONNECTION_AUTH (auth), msg, conn);
+}
+
+static void
+soup_connection_auth_class_init (SoupConnectionAuthClass *connauth_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (connauth_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (connauth_class);
+
+ g_type_class_add_private (connauth_class, sizeof (SoupConnectionAuthPrivate));
+
+ auth_class->update = soup_connection_auth_update;
+ auth_class->get_authorization = soup_connection_auth_get_authorization;
+ auth_class->is_ready = soup_connection_auth_is_ready;
+
+ object_class->finalize = soup_connection_auth_finalize;
+}
diff --git a/libsoup/soup-connection-auth.h b/libsoup/soup-connection-auth.h
new file mode 100644
index 00000000..251ca359
--- /dev/null
+++ b/libsoup/soup-connection-auth.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef SOUP_CONNECTION_AUTH_H
+#define SOUP_CONNECTION_AUTH_H 1
+
+#include <libsoup/soup-auth.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONNECTION_AUTH (soup_connection_auth_get_type ())
+#define SOUP_CONNECTION_AUTH(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuth))
+#define SOUP_CONNECTION_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuthClass))
+#define SOUP_IS_CONNECTION_AUTH(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_CONNECTION_AUTH))
+#define SOUP_IS_CONNECTION_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_CONNECTION_AUTH))
+#define SOUP_CONNECTION_AUTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuthClass))
+
+typedef struct SoupConnectionAuthPrivate SoupConnectionAuthPrivate;
+
+typedef struct {
+ SoupAuth parent;
+
+ SoupConnectionAuthPrivate *priv;
+} SoupConnectionAuth;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+ gpointer (*create_connection_state) (SoupConnectionAuth *auth);
+ void (*free_connection_state) (SoupConnectionAuth *auth,
+ gpointer conn);
+
+ gboolean (*update_connection) (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ const char *auth_header,
+ gpointer conn);
+ char *(*get_connection_authorization) (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer conn);
+ gboolean (*is_connection_ready) (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer conn);
+} SoupConnectionAuthClass;
+
+SOUP_AVAILABLE_IN_2_42
+GType soup_connection_auth_get_type (void);
+
+G_END_DECLS
+
+#endif /* SOUP_CONNECTION_AUTH_H */
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index 48c3d00c..fce589fa 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -11,23 +11,22 @@
#include "soup-connection.h"
#include "soup.h"
-#include "soup-marshal.h"
#include "soup-message-queue.h"
#include "soup-misc-private.h"
typedef struct {
SoupSocket *socket;
+ SoupAddress *local_addr;
SoupURI *remote_uri, *proxy_uri;
- SoupProxyURIResolver *proxy_resolver;
- gboolean use_gproxyresolver;
+ GProxyResolver *proxy_resolver;
GTlsDatabase *tlsdb;
gboolean ssl, ssl_strict, ssl_fallback;
GMainContext *async_context;
gboolean use_thread_context;
- SoupMessageQueueItem *cur_item;
+ SoupMessage *current_msg;
SoupConnectionState state;
time_t unused_timeout;
guint io_timeout, idle_timeout;
@@ -49,6 +48,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
+ PROP_LOCAL_ADDRESS,
PROP_REMOTE_URI,
PROP_PROXY_RESOLVER,
PROP_SSL,
@@ -60,13 +60,11 @@ enum {
PROP_TIMEOUT,
PROP_IDLE_TIMEOUT,
PROP_STATE,
- PROP_MESSAGE,
LAST_PROP
};
static void stop_idle_timer (SoupConnectionPrivate *priv);
-static void clear_current_item (SoupConnection *conn);
/* Number of seconds after which we close a connection that hasn't yet
* been used.
@@ -87,6 +85,7 @@ soup_connection_finalize (GObject *object)
g_clear_pointer (&priv->proxy_uri, soup_uri_free);
g_clear_object (&priv->tlsdb);
g_clear_object (&priv->proxy_resolver);
+ g_clear_object (&priv->local_addr);
g_clear_pointer (&priv->async_context, g_main_context_unref);
G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
@@ -99,13 +98,7 @@ soup_connection_dispose (GObject *object)
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
stop_idle_timer (priv);
- /* Make sure clear_current_item doesn't re-establish the timeout */
- priv->idle_timeout = 0;
- if (priv->cur_item) {
- g_warning ("Disposing connection with cur_item set");
- clear_current_item (conn);
- }
if (priv->socket) {
g_warning ("Disposing connection while connected");
soup_connection_disconnect (conn);
@@ -119,18 +112,16 @@ soup_connection_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
- SoupProxyURIResolver *proxy_resolver;
switch (prop_id) {
+ case PROP_LOCAL_ADDRESS:
+ priv->local_addr = g_value_dup_object (value);
+ break;
case PROP_REMOTE_URI:
priv->remote_uri = g_value_dup_boxed (value);
break;
case PROP_PROXY_RESOLVER:
- proxy_resolver = g_value_get_object (value);
- if (proxy_resolver && SOUP_IS_PROXY_RESOLVER_DEFAULT (proxy_resolver))
- priv->use_gproxyresolver = TRUE;
- else if (proxy_resolver)
- priv->proxy_resolver = g_object_ref (proxy_resolver);
+ priv->proxy_resolver = g_value_dup_object (value);
break;
case PROP_SSL:
priv->ssl = g_value_get_boolean (value);
@@ -176,6 +167,9 @@ soup_connection_get_property (GObject *object, guint prop_id,
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
switch (prop_id) {
+ case PROP_LOCAL_ADDRESS:
+ g_value_set_object (value, priv->local_addr);
+ break;
case PROP_REMOTE_URI:
g_value_set_boxed (value, priv->remote_uri);
break;
@@ -206,12 +200,6 @@ soup_connection_get_property (GObject *object, guint prop_id,
case PROP_STATE:
g_value_set_enum (value, priv->state);
break;
- case PROP_MESSAGE:
- if (priv->cur_item)
- g_value_set_object (value, priv->cur_item->msg);
- else
- g_value_set_object (value, NULL);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -248,11 +236,18 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/* properties */
g_object_class_install_property (
+ object_class, PROP_LOCAL_ADDRESS,
+ g_param_spec_object (SOUP_CONNECTION_LOCAL_ADDRESS,
+ "Local address",
+ "Address of local end of socket",
+ SOUP_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
object_class, PROP_REMOTE_URI,
g_param_spec_boxed (SOUP_CONNECTION_REMOTE_URI,
"Remote URI",
@@ -263,8 +258,8 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
object_class, PROP_PROXY_RESOLVER,
g_param_spec_object (SOUP_CONNECTION_PROXY_RESOLVER,
"Proxy resolver",
- "SoupProxyURIResolver to use",
- SOUP_TYPE_PROXY_URI_RESOLVER,
+ "GProxyResolver to use",
+ G_TYPE_PROXY_RESOLVER,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
object_class, PROP_SSL,
@@ -328,13 +323,20 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
"Current state of connection",
SOUP_TYPE_CONNECTION_STATE, SOUP_CONNECTION_NEW,
G_PARAM_READWRITE));
- g_object_class_install_property (
- object_class, PROP_MESSAGE,
- g_param_spec_object (SOUP_CONNECTION_MESSAGE,
- "Message",
- "Message being processed",
- SOUP_TYPE_MESSAGE,
- G_PARAM_READABLE));
+}
+
+static void
+soup_connection_event (SoupConnection *conn,
+ GSocketClientEvent event,
+ GIOStream *connection)
+{
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ if (!connection && priv->socket)
+ connection = soup_socket_get_connection (priv->socket);
+
+ g_signal_emit (conn, signals[EVENT], 0,
+ event, connection);
}
static gboolean
@@ -367,99 +369,71 @@ stop_idle_timer (SoupConnectionPrivate *priv)
}
static void
-current_item_restarted (SoupMessage *msg, gpointer user_data)
+current_msg_got_body (SoupMessage *msg, gpointer user_data)
{
SoupConnection *conn = user_data;
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
priv->unused_timeout = 0;
+
+ if (priv->proxy_uri &&
+ msg->method == SOUP_METHOD_CONNECT &&
+ SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATED, NULL);
+
+ /* We're now effectively no longer proxying */
+ g_clear_pointer (&priv->proxy_uri, soup_uri_free);
+ }
+
+ priv->reusable = soup_message_is_keepalive (msg);
}
static void
-set_current_item (SoupConnection *conn, SoupMessageQueueItem *item)
+clear_current_msg (SoupConnection *conn)
{
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ SoupMessage *msg;
- g_return_if_fail (priv->cur_item == NULL);
-
- g_object_freeze_notify (G_OBJECT (conn));
-
- stop_idle_timer (priv);
-
- item->state = SOUP_MESSAGE_RUNNING;
- priv->cur_item = item;
- g_object_notify (G_OBJECT (conn), "message");
- priv->reusable = FALSE;
-
- g_signal_connect (item->msg, "restarted",
- G_CALLBACK (current_item_restarted), conn);
-
- if (item->msg->method == SOUP_METHOD_CONNECT) {
- g_signal_emit (conn, signals[EVENT], 0,
- G_SOCKET_CLIENT_PROXY_NEGOTIATING,
- soup_socket_get_connection (priv->socket));
- } else if (priv->state == SOUP_CONNECTION_IDLE)
- soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+ msg = priv->current_msg;
+ priv->current_msg = NULL;
- g_object_thaw_notify (G_OBJECT (conn));
+ g_signal_handlers_disconnect_by_func (msg, G_CALLBACK (current_msg_got_body), conn);
+ g_object_unref (msg);
}
static void
-clear_current_item (SoupConnection *conn)
+set_current_msg (SoupConnection *conn, SoupMessage *msg)
{
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- g_object_freeze_notify (G_OBJECT (conn));
+ g_return_if_fail (priv->state == SOUP_CONNECTION_IN_USE);
- priv->unused_timeout = 0;
- start_idle_timer (conn);
+ g_object_freeze_notify (G_OBJECT (conn));
- if (priv->cur_item) {
- SoupMessageQueueItem *item;
+ if (priv->current_msg) {
+ g_return_if_fail (priv->current_msg->method == SOUP_METHOD_CONNECT);
+ clear_current_msg (conn);
+ }
- item = priv->cur_item;
- priv->cur_item = NULL;
- g_object_notify (G_OBJECT (conn), "message");
+ stop_idle_timer (priv);
- g_signal_handlers_disconnect_by_func (item->msg, G_CALLBACK (current_item_restarted), conn);
+ priv->current_msg = g_object_ref (msg);
+ priv->reusable = FALSE;
- if (item->msg->method == SOUP_METHOD_CONNECT &&
- SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code)) {
- g_signal_emit (conn, signals[EVENT], 0,
- G_SOCKET_CLIENT_PROXY_NEGOTIATED,
- soup_socket_get_connection (priv->socket));
+ g_signal_connect (msg, "got-body",
+ G_CALLBACK (current_msg_got_body), conn);
- /* We're now effectively no longer proxying */
- soup_uri_free (priv->proxy_uri);
- priv->proxy_uri = NULL;
- }
-
- if (!soup_message_is_keepalive (item->msg) || !priv->reusable)
- soup_connection_disconnect (conn);
- }
+ if (priv->proxy_uri && msg->method == SOUP_METHOD_CONNECT)
+ soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATING, NULL);
g_object_thaw_notify (G_OBJECT (conn));
}
static void
-soup_connection_event (SoupConnection *conn,
- GSocketClientEvent event,
- GIOStream *connection)
-{
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
-
- if (!connection && priv->socket)
- connection = soup_socket_get_connection (priv->socket);
-
- g_signal_emit (conn, signals[EVENT], 0,
- event, connection);
-}
-
-static void
-proxy_socket_event (SoupSocket *socket,
- GSocketClientEvent event,
- GIOStream *connection,
- gpointer user_data)
+re_emit_socket_event (SoupSocket *socket,
+ GSocketClientEvent event,
+ GIOStream *connection,
+ gpointer user_data)
{
SoupConnection *conn = user_data;
@@ -469,98 +443,103 @@ proxy_socket_event (SoupSocket *socket,
}
static void
-socket_disconnected (SoupSocket *sock, gpointer conn)
-{
- soup_connection_disconnect (conn);
-}
-
-typedef struct {
- SoupConnection *conn;
- SoupConnectionCallback callback;
- gpointer callback_data;
- GCancellable *cancellable;
- guint event_id;
-} SoupConnectionAsyncConnectData;
-
-static void
-socket_connect_finished (SoupSocket *socket, guint status, gpointer user_data)
+socket_connect_finished (GTask *task, SoupSocket *sock, GError *error)
{
- SoupConnectionAsyncConnectData *data = user_data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+ SoupConnection *conn = g_task_get_source_object (task);
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- g_signal_handler_disconnect (socket, data->event_id);
+ if (priv->async_context && !priv->use_thread_context)
+ g_main_context_pop_thread_default (priv->async_context);
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- g_signal_connect (priv->socket, "disconnected",
- G_CALLBACK (socket_disconnected), data->conn);
+ g_signal_handlers_disconnect_by_func (sock, G_CALLBACK (re_emit_socket_event), conn);
+ if (!error) {
if (priv->ssl && !priv->proxy_uri) {
- soup_connection_event (data->conn,
+ soup_connection_event (conn,
G_SOCKET_CLIENT_TLS_HANDSHAKED,
NULL);
}
if (!priv->ssl || !priv->proxy_uri) {
- soup_connection_event (data->conn,
+ soup_connection_event (conn,
G_SOCKET_CLIENT_COMPLETE,
NULL);
}
- soup_connection_set_state (data->conn, SOUP_CONNECTION_IN_USE);
+ soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
- start_idle_timer (data->conn);
- } else if (status == SOUP_STATUS_TLS_FAILED) {
- priv->ssl_fallback = TRUE;
- status = SOUP_STATUS_TRY_AGAIN;
- }
+ start_idle_timer (conn);
- if (data->callback) {
- if (priv->proxy_uri != NULL)
- status = soup_status_proxify (status);
- data->callback (data->conn, status, data->callback_data);
- }
- g_object_unref (data->conn);
- if (data->cancellable)
- g_object_unref (data->cancellable);
- g_slice_free (SoupConnectionAsyncConnectData, data);
+ g_task_return_boolean (task, TRUE);
+ } else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+socket_handshake_complete (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ SoupSocket *sock = SOUP_SOCKET (object);
+ GTask *task = user_data;
+ GError *error = NULL;
+
+ soup_socket_handshake_finish (sock, result, &error);
+ socket_connect_finished (task, sock, error);
}
static void
-socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
+socket_connect_complete (GObject *object, GAsyncResult *result, gpointer user_data)
{
- SoupConnectionAsyncConnectData *data = user_data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+ SoupSocket *sock = SOUP_SOCKET (object);
+ GTask *task = user_data;
+ SoupConnection *conn = g_task_get_source_object (task);
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ GError *error = NULL;
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- socket_connect_finished (sock, status, data);
+ if (!soup_socket_connect_finish_internal (sock, result, &error)) {
+ socket_connect_finished (task, sock, error);
return;
}
- if (priv->use_gproxyresolver)
- priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
+ priv->proxy_uri = soup_socket_get_http_proxy_uri (sock);
if (priv->ssl && !priv->proxy_uri) {
- if (soup_socket_start_ssl (sock, data->cancellable)) {
- soup_connection_event (data->conn,
- G_SOCKET_CLIENT_TLS_HANDSHAKING,
- NULL);
- soup_socket_handshake_async (sock, data->cancellable,
- socket_connect_finished, data);
- return;
- }
+ soup_connection_event (conn,
+ G_SOCKET_CLIENT_TLS_HANDSHAKING,
+ NULL);
- status = SOUP_STATUS_SSL_FAILED;
+ soup_socket_handshake_async (sock, priv->remote_uri->host,
+ g_task_get_cancellable (task),
+ socket_handshake_complete, task);
+ return;
}
- socket_connect_finished (sock, status, data);
+ socket_connect_finished (task, sock, NULL);
}
-static void
-connect_async_to_uri (SoupConnectionAsyncConnectData *data, SoupURI *uri)
+void
+soup_connection_connect_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+ SoupConnectionPrivate *priv;
SoupAddress *remote_addr;
+ GTask *task;
+
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ g_return_if_fail (priv->socket == NULL);
+
+ soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
+
+ /* Set the protocol to ensure correct proxy resolution. */
+ remote_addr =
+ g_object_new (SOUP_TYPE_ADDRESS,
+ SOUP_ADDRESS_NAME, priv->remote_uri->host,
+ SOUP_ADDRESS_PORT, priv->remote_uri->port,
+ SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme,
+ NULL);
- remote_addr = soup_address_new (uri->host, uri->port);
priv->socket =
soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
SOUP_SOCKET_SSL_CREDENTIALS, priv->tlsdb,
@@ -568,178 +547,107 @@ connect_async_to_uri (SoupConnectionAsyncConnectData *data, SoupURI *uri)
SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
SOUP_SOCKET_USE_THREAD_CONTEXT, priv->use_thread_context,
- SOUP_SOCKET_USE_PROXY, priv->use_gproxyresolver,
+ SOUP_SOCKET_PROXY_RESOLVER, priv->proxy_resolver,
SOUP_SOCKET_TIMEOUT, priv->io_timeout,
SOUP_SOCKET_CLEAN_DISPOSE, TRUE,
+ SOUP_SOCKET_LOCAL_ADDRESS, priv->local_addr,
NULL);
g_object_unref (remote_addr);
- data->event_id = g_signal_connect (priv->socket, "event",
- G_CALLBACK (proxy_socket_event),
- data->conn);
-
- soup_socket_connect_async (priv->socket, data->cancellable,
- socket_connect_result, data);
-}
-
-static void
-proxy_resolver_result (SoupProxyURIResolver *resolver,
- guint status, SoupURI *proxy_uri,
- gpointer user_data)
-{
- SoupConnectionAsyncConnectData *data = user_data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+ g_signal_connect (priv->socket, "event",
+ G_CALLBACK (re_emit_socket_event), conn);
- if (status != SOUP_STATUS_OK) {
- socket_connect_finished (NULL, status, data);
- return;
- }
+ if (priv->async_context && !priv->use_thread_context)
+ g_main_context_push_thread_default (priv->async_context);
+ task = g_task_new (conn, cancellable, callback, user_data);
- if (proxy_uri) {
- priv->proxy_uri = soup_uri_copy (proxy_uri);
- connect_async_to_uri (data, proxy_uri);
- } else
- connect_async_to_uri (data, priv->remote_uri);
+ soup_socket_connect_async_internal (priv->socket, cancellable,
+ socket_connect_complete, task);
}
-void
-soup_connection_connect_async (SoupConnection *conn,
- GCancellable *cancellable,
- SoupConnectionCallback callback,
- gpointer user_data)
+gboolean
+soup_connection_connect_finish (SoupConnection *conn,
+ GAsyncResult *result,
+ GError **error)
{
- SoupConnectionAsyncConnectData *data;
- SoupConnectionPrivate *priv;
- GMainContext *async_context;
-
- g_return_if_fail (SOUP_IS_CONNECTION (conn));
- priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- g_return_if_fail (priv->socket == NULL);
-
- soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
-
- data = g_slice_new (SoupConnectionAsyncConnectData);
- data->conn = g_object_ref (conn);
- data->callback = callback;
- data->callback_data = user_data;
- data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-
- if (!priv->proxy_resolver) {
- connect_async_to_uri (data, priv->remote_uri);
- return;
- }
-
- if (priv->use_thread_context)
- async_context = g_main_context_get_thread_default ();
- else
- async_context = priv->async_context;
-
- soup_proxy_uri_resolver_get_proxy_uri_async (priv->proxy_resolver,
- priv->remote_uri,
- async_context,
- cancellable,
- proxy_resolver_result,
- data);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
-guint
-soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
+gboolean
+soup_connection_connect_sync (SoupConnection *conn,
+ GCancellable *cancellable,
+ GError **error)
{
SoupConnectionPrivate *priv;
- guint status, event_id;
- SoupURI *connect_uri;
+ guint event_id = 0;
SoupAddress *remote_addr;
+ gboolean success = TRUE;
- g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (priv->socket == NULL, FALSE);
soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
- if (priv->proxy_resolver) {
- status = soup_proxy_uri_resolver_get_proxy_uri_sync (priv->proxy_resolver,
- priv->remote_uri,
- cancellable,
- &priv->proxy_uri);
- if (status != SOUP_STATUS_OK)
- goto fail;
-
- if (priv->proxy_uri)
- connect_uri = priv->proxy_uri;
- else
- connect_uri = priv->remote_uri;
- } else
- connect_uri = priv->remote_uri;
+ /* Set the protocol to ensure correct proxy resolution. */
+ remote_addr =
+ g_object_new (SOUP_TYPE_ADDRESS,
+ SOUP_ADDRESS_NAME, priv->remote_uri->host,
+ SOUP_ADDRESS_PORT, priv->remote_uri->port,
+ SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme,
+ NULL);
- remote_addr = soup_address_new (connect_uri->host, connect_uri->port);
priv->socket =
soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
- SOUP_SOCKET_USE_PROXY, priv->use_gproxyresolver,
+ SOUP_SOCKET_PROXY_RESOLVER, priv->proxy_resolver,
SOUP_SOCKET_SSL_CREDENTIALS, priv->tlsdb,
SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback,
SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
SOUP_SOCKET_TIMEOUT, priv->io_timeout,
SOUP_SOCKET_CLEAN_DISPOSE, TRUE,
+ SOUP_SOCKET_LOCAL_ADDRESS, priv->local_addr,
NULL);
g_object_unref (remote_addr);
event_id = g_signal_connect (priv->socket, "event",
- G_CALLBACK (proxy_socket_event), conn);
- status = soup_socket_connect_sync (priv->socket, cancellable);
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (status))
- goto fail;
+ G_CALLBACK (re_emit_socket_event), conn);
+ if (!soup_socket_connect_sync_internal (priv->socket, cancellable, error)) {
+ success = FALSE;
+ goto done;
+ }
- if (priv->use_gproxyresolver)
- priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
+ priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
if (priv->ssl && !priv->proxy_uri) {
- if (!soup_socket_start_ssl (priv->socket, cancellable))
- status = SOUP_STATUS_SSL_FAILED;
- else {
- soup_connection_event (conn,
- G_SOCKET_CLIENT_TLS_HANDSHAKING,
- NULL);
- status = soup_socket_handshake_sync (priv->socket, cancellable);
- if (status == SOUP_STATUS_OK) {
- soup_connection_event (conn,
- G_SOCKET_CLIENT_TLS_HANDSHAKED,
- NULL);
- } else if (status == SOUP_STATUS_TLS_FAILED) {
- priv->ssl_fallback = TRUE;
- status = SOUP_STATUS_TRY_AGAIN;
- }
+ soup_connection_event (conn,
+ G_SOCKET_CLIENT_TLS_HANDSHAKING,
+ NULL);
+ if (!soup_socket_handshake_sync (priv->socket,
+ priv->remote_uri->host,
+ cancellable, error)) {
+ success = FALSE;
+ goto done;
}
+ soup_connection_event (conn,
+ G_SOCKET_CLIENT_TLS_HANDSHAKED,
+ NULL);
}
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- g_signal_connect (priv->socket, "disconnected",
- G_CALLBACK (socket_disconnected), conn);
-
- if (!priv->ssl || !priv->proxy_uri) {
- soup_connection_event (conn,
- G_SOCKET_CLIENT_COMPLETE,
- NULL);
- }
- soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
- priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
- start_idle_timer (conn);
- } else {
- fail:
- if (priv->socket) {
- soup_socket_disconnect (priv->socket);
- g_object_unref (priv->socket);
- priv->socket = NULL;
- }
+ if (!priv->ssl || !priv->proxy_uri) {
+ soup_connection_event (conn,
+ G_SOCKET_CLIENT_COMPLETE,
+ NULL);
}
+ soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+ priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
+ start_idle_timer (conn);
- if (priv->socket)
+ done:
+ if (priv->socket && event_id)
g_signal_handler_disconnect (priv->socket, event_id);
- if (priv->proxy_uri != NULL)
- status = soup_status_proxify (status);
- return status;
+ return success;
}
gboolean
@@ -753,94 +661,74 @@ soup_connection_is_tunnelled (SoupConnection *conn)
return priv->ssl && priv->proxy_uri != NULL;
}
-guint
-soup_connection_start_ssl_sync (SoupConnection *conn,
- GCancellable *cancellable)
+gboolean
+soup_connection_start_ssl_sync (SoupConnection *conn,
+ GCancellable *cancellable,
+ GError **error)
{
SoupConnectionPrivate *priv;
- guint status;
g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- if (!soup_socket_start_proxy_ssl (priv->socket,
- priv->remote_uri->host,
- cancellable))
- return SOUP_STATUS_SSL_FAILED;
-
soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
- status = soup_socket_handshake_sync (priv->socket, cancellable);
- if (status == SOUP_STATUS_OK)
+ if (soup_socket_handshake_sync (priv->socket, priv->remote_uri->host,
+ cancellable, error)) {
soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
- else if (status == SOUP_STATUS_TLS_FAILED) {
- priv->ssl_fallback = TRUE;
- status = SOUP_STATUS_TRY_AGAIN;
- }
-
- return status;
+ soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL);
+ return TRUE;
+ } else
+ return FALSE;
}
static void
-start_ssl_completed (SoupSocket *socket, guint status, gpointer user_data)
+start_ssl_completed (GObject *object, GAsyncResult *result, gpointer user_data)
{
- SoupConnectionAsyncConnectData *data = user_data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
-
- if (status == SOUP_STATUS_OK)
- soup_connection_event (data->conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
- else if (status == SOUP_STATUS_TLS_FAILED) {
- priv->ssl_fallback = TRUE;
- status = SOUP_STATUS_TRY_AGAIN;
- }
-
- data->callback (data->conn, status, data->callback_data);
- g_object_unref (data->conn);
- g_slice_free (SoupConnectionAsyncConnectData, data);
-}
+ GTask *task = user_data;
+ SoupConnection *conn = g_task_get_source_object (task);
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ GError *error = NULL;
-static gboolean
-idle_start_ssl_completed (gpointer user_data)
-{
- SoupConnectionAsyncConnectData *data = user_data;
+ if (priv->async_context && !priv->use_thread_context)
+ g_main_context_pop_thread_default (priv->async_context);
- start_ssl_completed (NULL, SOUP_STATUS_SSL_FAILED, data);
- return FALSE;
+ if (soup_socket_handshake_finish (priv->socket, result, &error)) {
+ soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
+ soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL);
+ g_task_return_boolean (task, TRUE);
+ } else
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
void
-soup_connection_start_ssl_async (SoupConnection *conn,
- GCancellable *cancellable,
- SoupConnectionCallback callback,
- gpointer user_data)
+soup_connection_start_ssl_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
SoupConnectionPrivate *priv;
- SoupConnectionAsyncConnectData *data;
- GMainContext *async_context;
+ GTask *task;
g_return_if_fail (SOUP_IS_CONNECTION (conn));
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- data = g_slice_new (SoupConnectionAsyncConnectData);
- data->conn = g_object_ref (conn);
- data->callback = callback;
- data->callback_data = user_data;
+ soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
- if (priv->use_thread_context)
- async_context = g_main_context_get_thread_default ();
- else
- async_context = priv->async_context;
+ if (priv->async_context && !priv->use_thread_context)
+ g_main_context_push_thread_default (priv->async_context);
+ task = g_task_new (conn, cancellable, callback, user_data);
- if (!soup_socket_start_proxy_ssl (priv->socket,
- priv->remote_uri->host,
- cancellable)) {
- soup_add_completion (async_context,
- idle_start_ssl_completed, data);
- return;
- }
+ soup_socket_handshake_async (priv->socket, priv->remote_uri->host,
+ cancellable, start_ssl_completed, task);
+}
- soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
- soup_socket_handshake_async (priv->socket, cancellable,
- start_ssl_completed, data);
+gboolean
+soup_connection_start_ssl_finish (SoupConnection *conn,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
}
/**
@@ -864,14 +752,9 @@ soup_connection_disconnect (SoupConnection *conn)
soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
if (priv->socket) {
- /* Set the socket to NULL at the beginning to avoid reentrancy
- * issues. soup_socket_disconnect() could trigger a reentrant
- * call unref'ing and disconnecting the socket twice.
- */
SoupSocket *socket = priv->socket;
+
priv->socket = NULL;
- g_signal_handlers_disconnect_by_func (socket,
- socket_disconnected, conn);
soup_socket_disconnect (socket);
g_object_unref (socket);
}
@@ -936,7 +819,6 @@ void
soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
{
SoupConnectionPrivate *priv;
- SoupConnectionState old_state;
g_return_if_fail (SOUP_IS_CONNECTION (conn));
g_return_if_fail (state >= SOUP_CONNECTION_NEW &&
@@ -945,21 +827,26 @@ soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
g_object_freeze_notify (G_OBJECT (conn));
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- old_state = priv->state;
- priv->state = state;
- if ((state == SOUP_CONNECTION_IDLE ||
- state == SOUP_CONNECTION_DISCONNECTED) &&
- old_state == SOUP_CONNECTION_IN_USE)
- clear_current_item (conn);
- g_object_notify (G_OBJECT (conn), "state");
- g_object_thaw_notify (G_OBJECT (conn));
-}
+ if (priv->current_msg) {
+ g_warn_if_fail (state == SOUP_CONNECTION_IDLE ||
+ state == SOUP_CONNECTION_DISCONNECTED);
+ clear_current_msg (conn);
+ }
-void
-soup_connection_set_reusable (SoupConnection *conn)
-{
- SOUP_CONNECTION_GET_PRIVATE (conn)->reusable = TRUE;
+ if (state == SOUP_CONNECTION_IDLE && !priv->reusable) {
+ /* This will recursively call set_state() */
+ soup_connection_disconnect (conn);
+ } else {
+ priv->state = state;
+
+ if (priv->state == SOUP_CONNECTION_IDLE)
+ start_idle_timer (conn);
+
+ g_object_notify (G_OBJECT (conn), "state");
+ }
+
+ g_object_thaw_notify (G_OBJECT (conn));
}
gboolean
@@ -987,9 +874,13 @@ soup_connection_send_request (SoupConnection *conn,
g_return_if_fail (SOUP_IS_CONNECTION (conn));
g_return_if_fail (item != NULL);
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- g_return_if_fail (priv->state != SOUP_CONNECTION_NEW && priv->state != SOUP_CONNECTION_DISCONNECTED);
+ g_return_if_fail (priv->state != SOUP_CONNECTION_NEW &&
+ priv->state != SOUP_CONNECTION_DISCONNECTED);
+
+ if (item->msg != priv->current_msg)
+ set_current_msg (conn, item->msg);
+ else
+ priv->reusable = FALSE;
- if (item != priv->cur_item)
- set_current_item (conn, item);
soup_message_send_request (item, completion_cb, user_data);
}
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index ad3a1b13..b70a8a36 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -35,10 +35,7 @@ typedef struct {
GType soup_connection_get_type (void);
-typedef void (*SoupConnectionCallback) (SoupConnection *conn,
- guint status,
- gpointer data);
-
+#define SOUP_CONNECTION_LOCAL_ADDRESS "local-address"
#define SOUP_CONNECTION_REMOTE_URI "remote-uri"
#define SOUP_CONNECTION_PROXY_RESOLVER "proxy-resolver"
#define SOUP_CONNECTION_SSL "ssl"
@@ -52,18 +49,26 @@ typedef void (*SoupConnectionCallback) (SoupConnection *conn,
#define SOUP_CONNECTION_STATE "state"
#define SOUP_CONNECTION_MESSAGE "message"
-void soup_connection_connect_async (SoupConnection *conn,
- GCancellable *cancellable,
- SoupConnectionCallback callback,
- gpointer user_data);
-guint soup_connection_connect_sync (SoupConnection *conn,
- GCancellable *cancellable);
-guint soup_connection_start_ssl_sync (SoupConnection *conn,
- GCancellable *cancellable);
-void soup_connection_start_ssl_async (SoupConnection *conn,
- GCancellable *cancellable,
- SoupConnectionCallback callback,
- gpointer user_data);
+void soup_connection_connect_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_connection_connect_finish (SoupConnection *conn,
+ GAsyncResult *result,
+ GError **error);
+gboolean soup_connection_connect_sync (SoupConnection *conn,
+ GCancellable *cancellable,
+ GError **error);
+gboolean soup_connection_start_ssl_sync (SoupConnection *conn,
+ GCancellable *cancellable,
+ GError **error);
+void soup_connection_start_ssl_async (SoupConnection *conn,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_connection_start_ssl_finish (SoupConnection *conn,
+ GAsyncResult *result,
+ GError **error);
void soup_connection_disconnect (SoupConnection *conn);
@@ -76,7 +81,6 @@ gboolean soup_connection_is_tunnelled (SoupConnection *conn);
SoupConnectionState soup_connection_get_state (SoupConnection *conn);
void soup_connection_set_state (SoupConnection *conn,
SoupConnectionState state);
-void soup_connection_set_reusable (SoupConnection *conn);
gboolean soup_connection_get_ever_used (SoupConnection *conn);
diff --git a/libsoup/soup-content-decoder.c b/libsoup/soup-content-decoder.c
index 85dcef49..bce78c01 100644
--- a/libsoup/soup-content-decoder.c
+++ b/libsoup/soup-content-decoder.c
@@ -10,6 +10,7 @@
#endif
#include "soup-content-decoder.h"
+#include "soup-converter-wrapper.h"
#include "soup.h"
#include "soup-message-private.h"
@@ -17,15 +18,18 @@
* SECTION:soup-content-decoder
* @short_description: Content-Encoding handler
*
- * #SoupContentDecoder handles the "Accept-Encoding" header on
- * outgoing messages, and the "Content-Encoding" header on incoming
- * ones. If you add it to a session with soup_session_add_feature() or
- * soup_session_add_feature_by_type(), the session will automatically
- * use Content-Encoding as appropriate.
+ * #SoupContentDecoder handles adding the "Accept-Encoding" header on
+ * outgoing messages, and processing the "Content-Encoding" header on
+ * incoming ones. Currently it supports the "gzip" and "deflate"
+ * content codings.
*
- * (Note that currently there is no way to (automatically) use
- * Content-Encoding when sending a request body, or to pick specific
- * encoding types to support.)
+ * If you are using a plain #SoupSession (ie, not #SoupSessionAsync or
+ * #SoupSessionSync), then a #SoupContentDecoder will automatically be
+ * added to the session by default. (You can use
+ * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE at construct time if you don't
+ * want this.) If you are using one of the deprecated #SoupSession
+ * subclasses, you can add a #SoupContentDecoder to your session with
+ * soup_session_add_feature() or soup_session_add_feature_by_type().
*
* If #SoupContentDecoder successfully decodes the Content-Encoding,
* it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message,
@@ -39,7 +43,11 @@
* will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will
* not be set).
*
- * Since: 2.28.2
+ * (Note that currently there is no way to (automatically) use
+ * Content-Encoding when sending a request body, or to pick specific
+ * encoding types to support.)
+ *
+ * Since: 2.30
**/
struct _SoupContentDecoderPrivate {
@@ -50,9 +58,114 @@ typedef GConverter * (*SoupContentDecoderCreator) (void);
static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupContentProcessorInterface *soup_content_decoder_default_content_processor_interface;
+static void soup_content_decoder_content_processor_init (SoupContentProcessorInterface *interface, gpointer interface_data);
+
+
G_DEFINE_TYPE_WITH_CODE (SoupContentDecoder, soup_content_decoder, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
- soup_content_decoder_session_feature_init))
+ soup_content_decoder_session_feature_init)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_CONTENT_PROCESSOR,
+ soup_content_decoder_content_processor_init))
+
+static GSList *
+soup_content_decoder_get_decoders_for_msg (SoupContentDecoder *decoder, SoupMessage *msg)
+{
+ const char *header;
+ GSList *encodings, *e, *decoders = NULL;
+ SoupContentDecoderCreator converter_creator;
+ GConverter *converter;
+
+ header = soup_message_headers_get_list (msg->response_headers,
+ "Content-Encoding");
+ if (!header)
+ return NULL;
+
+ /* Workaround for an apache bug (bgo 613361) */
+ if (!g_ascii_strcasecmp (header, "gzip") ||
+ !g_ascii_strcasecmp (header, "x-gzip")) {
+ const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
+
+ if (content_type &&
+ (!g_ascii_strcasecmp (content_type, "application/gzip") ||
+ !g_ascii_strcasecmp (content_type, "application/x-gzip")))
+ return NULL;
+ }
+
+ /* OK, really, no one is ever going to use more than one
+ * encoding, but we'll be robust.
+ */
+ encodings = soup_header_parse_list (header);
+ if (!encodings)
+ return NULL;
+
+ for (e = encodings; e; e = e->next) {
+ if (!g_hash_table_lookup (decoder->priv->decoders, e->data)) {
+ soup_header_free_list (encodings);
+ return NULL;
+ }
+ }
+
+ for (e = encodings; e; e = e->next) {
+ converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data);
+ converter = converter_creator ();
+
+ /* Content-Encoding lists the codings in the order
+ * they were applied in, so we put decoders in reverse
+ * order so the last-applied will be the first
+ * decoded.
+ */
+ decoders = g_slist_prepend (decoders, converter);
+ }
+ soup_header_free_list (encodings);
+
+ return decoders;
+}
+
+static GInputStream*
+soup_content_decoder_content_processor_wrap_input (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error)
+{
+ GSList *decoders, *d;
+ GInputStream *istream;
+
+ decoders = soup_content_decoder_get_decoders_for_msg (SOUP_CONTENT_DECODER (processor), msg);
+ if (!decoders)
+ return NULL;
+
+ istream = g_object_ref (base_stream);
+ for (d = decoders; d; d = d->next) {
+ GConverter *decoder, *wrapper;
+ GInputStream *filter;
+
+ decoder = d->data;
+ wrapper = soup_converter_wrapper_new (decoder, msg);
+ filter = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
+ "base-stream", istream,
+ "converter", wrapper,
+ NULL);
+ g_object_unref (istream);
+ g_object_unref (wrapper);
+ istream = filter;
+ }
+
+ g_slist_free_full (decoders, g_object_unref);
+
+ return istream;
+}
+
+static void
+soup_content_decoder_content_processor_init (SoupContentProcessorInterface *processor_interface,
+ gpointer interface_data)
+{
+ soup_content_decoder_default_content_processor_interface =
+ g_type_default_interface_peek (SOUP_TYPE_CONTENT_PROCESSOR);
+
+ processor_interface->processing_stage = SOUP_STAGE_CONTENT_ENCODING;
+ processor_interface->wrap_input = soup_content_decoder_content_processor_wrap_input;
+}
/* This is constant for now */
#define ACCEPT_ENCODING_HEADER "gzip, deflate"
@@ -107,89 +220,16 @@ soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class)
}
static void
-soup_content_decoder_got_headers_cb (SoupMessage *msg, SoupContentDecoder *decoder)
-{
- SoupMessagePrivate *msgpriv = SOUP_MESSAGE_GET_PRIVATE (msg);
- const char *header;
- GSList *encodings, *e;
- SoupContentDecoderCreator converter_creator;
- GConverter *converter;
-
- header = soup_message_headers_get_list (msg->response_headers,
- "Content-Encoding");
- if (!header)
- return;
-
- /* Workaround for an apache bug (bgo 613361) */
- if (!g_ascii_strcasecmp (header, "gzip") ||
- !g_ascii_strcasecmp (header, "x-gzip")) {
- const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
-
- if (content_type &&
- (!g_ascii_strcasecmp (content_type, "application/gzip") ||
- !g_ascii_strcasecmp (content_type, "application/x-gzip")))
- return;
- }
-
- /* OK, really, no one is ever going to use more than one
- * encoding, but we'll be robust.
- */
- encodings = soup_header_parse_list (header);
- if (!encodings)
- return;
-
- for (e = encodings; e; e = e->next) {
- if (!g_hash_table_lookup (decoder->priv->decoders, e->data)) {
- soup_header_free_list (encodings);
- return;
- }
- }
-
- /* msgpriv->decoders should be empty at this point anyway, but
- * clean it up if it's not.
- */
- g_slist_free_full (msgpriv->decoders, g_object_unref);
- msgpriv->decoders = NULL;
-
- for (e = encodings; e; e = e->next) {
- converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data);
- converter = converter_creator ();
-
- /* Content-Encoding lists the codings in the order
- * they were applied in, so we put decoders in reverse
- * order so the last-applied will be the first
- * decoded.
- */
- msgpriv->decoders = g_slist_prepend (msgpriv->decoders, converter);
- }
- soup_header_free_list (encodings);
-}
-
-static void
soup_content_decoder_request_queued (SoupSessionFeature *feature,
SoupSession *session,
SoupMessage *msg)
{
- SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (feature);
-
if (!soup_message_headers_get_one (msg->request_headers,
"Accept-Encoding")) {
soup_message_headers_append (msg->request_headers,
"Accept-Encoding",
ACCEPT_ENCODING_HEADER);
}
-
- g_signal_connect (msg, "got-headers",
- G_CALLBACK (soup_content_decoder_got_headers_cb),
- decoder);
-}
-
-static void
-soup_content_decoder_request_unqueued (SoupSessionFeature *feature,
- SoupSession *session,
- SoupMessage *msg)
-{
- g_signal_handlers_disconnect_by_func (msg, soup_content_decoder_got_headers_cb, feature);
}
static void
@@ -197,5 +237,4 @@ soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_
gpointer interface_data)
{
feature_interface->request_queued = soup_content_decoder_request_queued;
- feature_interface->request_unqueued = soup_content_decoder_request_unqueued;
}
diff --git a/libsoup/soup-content-decoder.h b/libsoup/soup-content-decoder.h
index e0b22383..471f8138 100644
--- a/libsoup/soup-content-decoder.h
+++ b/libsoup/soup-content-decoder.h
@@ -37,7 +37,8 @@ typedef struct {
void (*_libsoup_reserved5) (void);
} SoupContentDecoderClass;
-GType soup_content_decoder_get_type (void);
+SOUP_AVAILABLE_IN_2_30
+GType soup_content_decoder_get_type (void);
G_END_DECLS
diff --git a/libsoup/soup-content-processor.c b/libsoup/soup-content-processor.c
new file mode 100644
index 00000000..8b959ebe
--- /dev/null
+++ b/libsoup/soup-content-processor.c
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012 Igalia, S.L.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-content-processor.h"
+#include "soup.h"
+
+static void soup_content_processor_default_init (SoupContentProcessorInterface *interface);
+
+G_DEFINE_INTERFACE (SoupContentProcessor, soup_content_processor, G_TYPE_OBJECT)
+
+static GInputStream *
+soup_content_processor_real_wrap_input (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error)
+{
+ g_return_val_if_reached (NULL);
+}
+
+static void
+soup_content_processor_default_init (SoupContentProcessorInterface *interface)
+{
+ interface->processing_stage = SOUP_STAGE_INVALID;
+ interface->wrap_input = soup_content_processor_real_wrap_input;
+}
+
+GInputStream *
+soup_content_processor_wrap_input (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error)
+{
+ g_return_val_if_fail (SOUP_IS_CONTENT_PROCESSOR (processor), NULL);
+
+ return SOUP_CONTENT_PROCESSOR_GET_INTERFACE (processor)->wrap_input (processor, base_stream, msg, error);
+}
+
+SoupProcessingStage
+soup_content_processor_get_processing_stage (SoupContentProcessor *processor)
+{
+ g_return_val_if_fail (SOUP_IS_CONTENT_PROCESSOR (processor), SOUP_STAGE_INVALID);
+
+ return SOUP_CONTENT_PROCESSOR_GET_INTERFACE (processor)->processing_stage;
+}
diff --git a/libsoup/soup-content-processor.h b/libsoup/soup-content-processor.h
new file mode 100644
index 00000000..8ee04b2f
--- /dev/null
+++ b/libsoup/soup-content-processor.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012 Igalia, S.L.
+ */
+
+
+#ifndef SOUP_CONTENT_PROCESSOR_H
+#define SOUP_CONTENT_PROCESSOR_H 1
+
+#include <libsoup/soup-types.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONTENT_PROCESSOR (soup_content_processor_get_type ())
+#define SOUP_CONTENT_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONTENT_PROCESSOR, SoupContentProcessor))
+#define SOUP_IS_CONTENT_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONTENT_PROCESSOR))
+#define SOUP_CONTENT_PROCESSOR_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), SOUP_TYPE_CONTENT_PROCESSOR, SoupContentProcessorInterface))
+
+typedef enum {
+ SOUP_STAGE_INVALID,
+
+ SOUP_STAGE_MESSAGE_BODY, /* Raw network data */
+ SOUP_STAGE_TRANSFER_ENCODING, /* SoupBodyInputStream is here */
+ SOUP_STAGE_ENTITY_BODY, /* Has Transfer-Encoding removed */
+ SOUP_STAGE_CONTENT_ENCODING, /* SoupContentDecoder works here */
+ SOUP_STAGE_BODY_DATA /* Actual body data */
+} SoupProcessingStage;
+
+typedef struct _SoupContentProcessor SoupContentProcessor;
+typedef struct _SoupContentProcessorInterface SoupContentProcessorInterface;
+
+struct _SoupContentProcessorInterface {
+ GTypeInterface parent;
+
+ SoupProcessingStage processing_stage;
+
+ /* methods */
+ GInputStream* (*wrap_input) (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error);
+};
+
+GType soup_content_processor_get_type (void);
+
+GInputStream *soup_content_processor_wrap_input (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error);
+
+SoupProcessingStage soup_content_processor_get_processing_stage (SoupContentProcessor *processor);
+
+G_END_DECLS
+
+#endif /* SOUP_CONTENT_PROCESSOR_H */
diff --git a/libsoup/soup-content-sniffer-stream.c b/libsoup/soup-content-sniffer-stream.c
index 42e92476..d358a19e 100644
--- a/libsoup/soup-content-sniffer-stream.c
+++ b/libsoup/soup-content-sniffer-stream.c
@@ -233,6 +233,16 @@ soup_content_sniffer_stream_skip (GInputStream *stream,
}
static gboolean
+soup_content_sniffer_stream_can_poll (GPollableInputStream *pollable)
+{
+ GInputStream *base_stream = G_FILTER_INPUT_STREAM (pollable)->base_stream;
+
+ return G_IS_POLLABLE_INPUT_STREAM (base_stream) &&
+ g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (base_stream));
+}
+
+
+static gboolean
soup_content_sniffer_stream_is_readable (GPollableInputStream *stream)
{
SoupContentSnifferStream *sniffer = SOUP_CONTENT_SNIFFER_STREAM (stream);
@@ -320,23 +330,12 @@ static void
soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
gpointer interface_data)
{
+ pollable_interface->can_poll = soup_content_sniffer_stream_can_poll;
pollable_interface->is_readable = soup_content_sniffer_stream_is_readable;
pollable_interface->read_nonblocking = soup_content_sniffer_stream_read_nonblocking;
pollable_interface->create_source = soup_content_sniffer_stream_create_source;
}
-GInputStream *
-soup_content_sniffer_stream_new (SoupContentSniffer *sniffer,
- SoupMessage *msg,
- GInputStream *base_stream)
-{
- return g_object_new (SOUP_TYPE_CONTENT_SNIFFER_STREAM,
- "base-stream", base_stream,
- "message", msg,
- "sniffer", sniffer,
- NULL);
-}
-
gboolean
soup_content_sniffer_stream_is_ready (SoupContentSnifferStream *sniffer,
gboolean blocking,
diff --git a/libsoup/soup-content-sniffer-stream.h b/libsoup/soup-content-sniffer-stream.h
index fb4889ce..ab230f3e 100644
--- a/libsoup/soup-content-sniffer-stream.h
+++ b/libsoup/soup-content-sniffer-stream.h
@@ -36,10 +36,6 @@ struct _SoupContentSnifferStreamClass {
GType soup_content_sniffer_stream_get_type (void) G_GNUC_CONST;
-GInputStream *soup_content_sniffer_stream_new (SoupContentSniffer *sniffer,
- SoupMessage *msg,
- GInputStream *base_stream);
-
gboolean soup_content_sniffer_stream_is_ready (SoupContentSnifferStream *sniffer,
gboolean blocking,
GCancellable *cancellable,
diff --git a/libsoup/soup-content-sniffer.c b/libsoup/soup-content-sniffer.c
index b5fd125e..5659af9a 100644
--- a/libsoup/soup-content-sniffer.c
+++ b/libsoup/soup-content-sniffer.c
@@ -2,7 +2,11 @@
/*
* soup-content-sniffer.c
*
- * Copyright (C) 2009 Gustavo Noronha Silva.
+ * Copyright (C) 2009, 2013 Gustavo Noronha Silva.
+ *
+ * This code implements the following specification:
+ *
+ * http://mimesniff.spec.whatwg.org/ as of 11 June 2013
*/
#ifdef HAVE_CONFIG_H
@@ -13,11 +17,13 @@
#include "soup-content-sniffer.h"
#include "soup.h"
+#include "soup-content-processor.h"
+#include "soup-content-sniffer-stream.h"
#include "soup-message-private.h"
/**
* SECTION:soup-content-sniffer
- * @short_description: Content sniffing for #SoupSession
+ * @short_description: Content sniffing for SoupSession
*
* A #SoupContentSniffer tries to detect the actual content type of
* the files that are being downloaded by looking at some of the data
@@ -26,26 +32,257 @@
* content sniffing to a session with soup_session_add_feature() or
* soup_session_add_feature_by_type().
*
- * Since: 2.27.3
+ * Since: 2.28
**/
static void soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupContentProcessorInterface *soup_content_sniffer_default_content_processor_interface;
+static void soup_content_sniffer_content_processor_init (SoupContentProcessorInterface *interface, gpointer interface_data);
+
+
G_DEFINE_TYPE_WITH_CODE (SoupContentSniffer, soup_content_sniffer, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
- soup_content_sniffer_session_feature_init))
+ soup_content_sniffer_session_feature_init)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_CONTENT_PROCESSOR,
+ soup_content_sniffer_content_processor_init))
+
+
+static GInputStream *
+soup_content_sniffer_content_processor_wrap_input (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error)
+{
+ return g_object_new (SOUP_TYPE_CONTENT_SNIFFER_STREAM,
+ "base-stream", base_stream,
+ "message", msg,
+ "sniffer", SOUP_CONTENT_SNIFFER (processor),
+ NULL);
+}
+
+static void
+soup_content_sniffer_content_processor_init (SoupContentProcessorInterface *processor_interface,
+ gpointer interface_data)
+{
+ soup_content_sniffer_default_content_processor_interface =
+ g_type_default_interface_peek (SOUP_TYPE_CONTENT_PROCESSOR);
+
+ processor_interface->processing_stage = SOUP_STAGE_BODY_DATA;
+ processor_interface->wrap_input = soup_content_sniffer_content_processor_wrap_input;
+}
static void
soup_content_sniffer_init (SoupContentSniffer *content_sniffer)
{
}
-/* This table is based on the HTML5 spec;
- * See 2.7.4 Content-Type sniffing: unknown type
+typedef struct {
+ const guchar *mask;
+ const guchar *pattern;
+ guint pattern_length;
+ const char *sniffed_type;
+} SoupContentSnifferMediaPattern;
+
+static char*
+sniff_media (SoupContentSniffer *sniffer,
+ SoupBuffer *buffer,
+ SoupContentSnifferMediaPattern table[],
+ int table_length)
+{
+ const guchar *resource = (const guchar *)buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ int i;
+
+ for (i = 0; i < table_length; i++) {
+ SoupContentSnifferMediaPattern *type_row = &(table[i]);
+ int j;
+
+ if (resource_length < type_row->pattern_length)
+ continue;
+
+ for (j = 0; j < type_row->pattern_length; j++) {
+ if ((type_row->mask[j] & resource[j]) != type_row->pattern[j])
+ break;
+ }
+
+ /* This means our comparison above matched completely */
+ if (j == type_row->pattern_length)
+ return g_strdup (type_row->sniffed_type);
+ }
+
+ return NULL;
+}
+
+/* This table is based on the MIMESNIFF spec;
+ * See 6.1 Matching an image type pattern
+ */
+static SoupContentSnifferMediaPattern image_types_table[] = {
+
+ /* Windows icon signature. */
+ { (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x00\x00\x01\x00",
+ 4,
+ "image/x-icon" },
+
+ /* Windows cursor signature. */
+ { (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x00\x00\x02\x00",
+ 4,
+ "image/x-icon" },
+
+ /* BMP. */
+ { (const guchar *)"\xFF\xFF",
+ (const guchar *)"BM",
+ 2,
+ "image/bmp" },
+
+ /* GIFs. */
+ { (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"GIF87a",
+ 6,
+ "image/gif" },
+
+ { (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"GIF89a",
+ 6,
+ "image/gif" },
+
+ /* WEBP. */
+ { (const guchar *)"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"RIFF\x00\x00\x00\x00WEBPVP",
+ 14,
+ "image/webp" },
+
+ /* PNG. */
+ { (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x89PNG\x0D\x0A\x1A\x0A",
+ 8,
+ "image/png" },
+
+ /* JPEG. */
+ { (const guchar *)"\xFF\xFF\xFF",
+ (const guchar *)"\xFF\xD8\xFF",
+ 3,
+ "image/jpeg" },
+};
+
+static char*
+sniff_images (SoupContentSniffer *sniffer, SoupBuffer *buffer)
+{
+ return sniff_media (sniffer,
+ buffer,
+ image_types_table,
+ G_N_ELEMENTS (image_types_table));
+}
+
+/* This table is based on the MIMESNIFF spec;
+ * See 6.2 Matching an audio or video type pattern
+ */
+static SoupContentSnifferMediaPattern audio_video_types_table[] = {
+ { (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)"\x1A\x45\xDF\xA3",
+ 4,
+ "video/webm" },
+
+ { (const guchar *)"\xFF\xFF\xFF\xFF",
+ (const guchar *)".snd",
+ 4,
+ "audio/basic" },
+
+
+ { (const guchar *)"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
+ (const guchar *)"FORM\0\0\0\0AIFF",
+ 12,
+ "audio/aiff" },
+
+ { (const guchar *)"\xFF\xFF\xFF",
+ (const guchar *)"ID3",
+ 3,
+ "audio/mpeg" },
+
+ { (const guchar *)"\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"OggS\0",
+ 5,
+ "application/ogg" },
+
+ { (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)"MThd\x00\x00\x00\x06",
+ 8,
+ "audio/midi" },
+
+ { (const guchar *)"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
+ (const guchar *)"RIFF\x00\x00\x00\x00AVI ",
+ 12,
+ "video/avi" },
+
+ { (const guchar *)"\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
+ (const guchar *)"RIFF\x00\x00\x00\x00WAVE",
+ 12,
+ "audio/wave" },
+};
+
+static gboolean
+sniff_mp4 (SoupContentSniffer *sniffer, SoupBuffer *buffer)
+{
+ const char *resource = (const char *)buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ guint32 box_size = *((guint32*)resource);
+ int i;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ box_size = ((box_size >> 24) |
+ ((box_size << 8) & 0x00FF0000) |
+ ((box_size >> 8) & 0x0000FF00) |
+ (box_size << 24));
+#endif
+
+ if (resource_length < 12 || resource_length < box_size || box_size % 4 != 0)
+ return FALSE;
+
+ if (!g_str_has_prefix (resource + 4, "ftyp"))
+ return FALSE;
+
+ if (!g_str_has_prefix (resource + 8, "mp4"))
+ return FALSE;
+
+ for (i = 16; i < box_size && i < resource_length; i = i + 4) {
+ if (g_str_has_prefix (resource + i, "mp4"))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static char*
+sniff_audio_video (SoupContentSniffer *sniffer, SoupBuffer *buffer)
+{
+ char *sniffed_type;
+
+ sniffed_type = sniff_media (sniffer,
+ buffer,
+ audio_video_types_table,
+ G_N_ELEMENTS (audio_video_types_table));
+
+ if (sniffed_type != NULL)
+ return sniffed_type;
+
+ if (sniff_mp4 (sniffer, buffer))
+ return g_strdup ("video/mp4");
+
+ return NULL;
+}
+
+/* This table is based on the MIMESNIFF spec;
+ * See 7.1 Identifying a resource with an unknown MIME type
*/
typedef struct {
/* @has_ws is TRUE if @pattern contains "generic" whitespace */
gboolean has_ws;
+ /* @has_tag_termination is TRUE if we should check for a tag-terminating
+ * byte (0x20 " " or 0x3E ">") after the pattern match.
+ */
+ gboolean has_tag_termination;
const guchar *mask;
const guchar *pattern;
guint pattern_length;
@@ -53,111 +290,174 @@ typedef struct {
gboolean scriptable;
} SoupContentSnifferPattern;
+
+/* When has_ws is TRUE, spaces in the pattern will indicate where insignificant space
+ * is allowed. Those spaces are marked with \x00 on the mask.
+ */
static SoupContentSnifferPattern types_table[] = {
- { FALSE,
- (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF",
- (const guchar *)"\x3C\x21\x44\x4F\x43\x54\x59\x50\x45\x20\x48\x54\x4D\x4C",
+ /* Scriptable types. */
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <!DOCTYPE HTML",
14,
"text/html",
TRUE },
- { TRUE,
- (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF",
- (const guchar *)" \x3C\x48\x54\x4D\x4C",
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <HTML",
5,
"text/html",
TRUE },
- { TRUE,
- (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF",
- (const guchar *)" \x3C\x48\x45\x41\x44",
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <HEAD",
5,
"text/html",
TRUE },
- { TRUE,
- (const guchar *)"\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
- (const guchar *)" \x3C\x53\x43\x52\x49\x50\x54",
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <SCRIPT",
+ 7,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <IFRAME",
7,
"text/html",
TRUE },
- { FALSE,
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xFF",
+ (const guchar *)" <H1",
+ 3,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF",
+ (const guchar *)" <DIV",
+ 4,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <FONT",
+ 5,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <TABLE",
+ 6,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF",
+ (const guchar *)" <A",
+ 2,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <STYLE",
+ 6,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <TITLE",
+ 6,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF",
+ (const guchar *)" <B",
+ 2,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF\xDF\xDF",
+ (const guchar *)" <BODY",
+ 5,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF\xDF",
+ (const guchar *)" <BR",
+ 3,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xDF",
+ (const guchar *)" <P",
+ 2,
+ "text/html",
+ TRUE },
+
+ { TRUE, TRUE,
+ (const guchar *)"\x00\xFF\xFF\xFF\xFF",
+ (const guchar *)" <!--",
+ 4,
+ "text/html",
+ TRUE },
+
+ { TRUE, FALSE,
+ (const guchar *)"\x00\xFF\xFF\xFF\xFF\xFF",
+ (const guchar *)" <?xml",
+ 5,
+ "text/html",
+ TRUE },
+
+ { FALSE, FALSE,
(const guchar *)"\xFF\xFF\xFF\xFF\xFF",
- (const guchar *)"\x25\x50\x44\x46\x2D",
+ (const guchar *)"%PDF-",
5,
"application/pdf",
TRUE },
- { FALSE,
+ /* Non-scriptable types. */
+ { FALSE, FALSE,
(const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
- (const guchar *)"\x25\x21\x50\x53\x2D\x41\x64\x6F\x62\x65\x2D",
+ (const guchar *)"%!PS-Adobe-",
11,
"application/postscript",
FALSE },
- { FALSE,
+ { FALSE, FALSE, /* UTF-16BE BOM */
(const guchar *)"\xFF\xFF\x00\x00",
(const guchar *)"\xFE\xFF\x00\x00",
4,
"text/plain",
FALSE },
- { FALSE,
- (const guchar *)"\xFF\xFF\x00\x00",
+ { FALSE, FALSE, /* UTF-16LE BOM */
(const guchar *)"\xFF\xFF\x00\x00",
+ (const guchar *)"\xFF\xFE\x00\x00",
4,
"text/plain",
FALSE },
- { FALSE,
+ { FALSE, FALSE, /* UTF-8 BOM */
(const guchar *)"\xFF\xFF\xFF\x00",
(const guchar *)"\xEF\xBB\xBF\x00",
4,
"text/plain",
FALSE },
-
- { FALSE,
- (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
- (const guchar *)"\x47\x49\x46\x38\x37\x61",
- 6,
- "image/gif",
- FALSE },
-
- { FALSE,
- (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF",
- (const guchar *)"\x47\x49\x46\x38\x39\x61",
- 6,
- "image/gif",
- FALSE },
-
- { FALSE,
- (const guchar *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
- (const guchar *)"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",
- 8,
- "image/png",
- FALSE },
-
- { FALSE,
- (const guchar *)"\xFF\xFF\xFF",
- (const guchar *)"\xFF\xD8\xFF",
- 3,
- "image/jpeg",
- FALSE },
-
- { FALSE,
- (const guchar *)"\xFF\xFF",
- (const guchar *)"\x42\x4D",
- 2,
- "image/bmp",
- FALSE },
-
- { FALSE,
- (const guchar *)"\xFF\xFF\xFF\xFF",
- (const guchar *)"\x00\x00\x01\x00",
- 4,
- "image/vnd.microsoft.icon",
- FALSE }
};
/* Whether a given byte looks like it might be part of binary content.
@@ -185,9 +485,10 @@ static char byte_looks_binary[] = {
/* HTML5: 2.7.4 Content-Type sniffing: unknown type */
static char*
-sniff_unknown (SoupContentSniffer *sniffer, SoupMessage *msg,
- SoupBuffer *buffer, gboolean for_text_or_binary)
+sniff_unknown (SoupContentSniffer *sniffer, SoupBuffer *buffer,
+ gboolean sniff_scriptable)
{
+ char *sniffed_type = NULL;
const guchar *resource = (const guchar *)buffer->data;
int resource_length = MIN (512, buffer->length);
int i;
@@ -195,9 +496,7 @@ sniff_unknown (SoupContentSniffer *sniffer, SoupMessage *msg,
for (i = 0; i < G_N_ELEMENTS (types_table); i++) {
SoupContentSnifferPattern *type_row = &(types_table[i]);
- /* The scriptable types should be skiped for the text
- * or binary path, but considered for other paths */
- if (for_text_or_binary && type_row->scriptable)
+ if (!sniff_scriptable && type_row->scriptable)
continue;
if (type_row->has_ws) {
@@ -230,8 +529,14 @@ sniff_unknown (SoupContentSniffer *sniffer, SoupMessage *msg,
if (skip_row)
continue;
- if (index_pattern > type_row->pattern_length)
+ if (index_pattern > type_row->pattern_length) {
+ if (type_row->has_tag_termination &&
+ resource[index_stream] != '\x20' &&
+ resource[index_stream] != '\x3E')
+ continue;
+
return g_strdup (type_row->sniffed_type);
+ }
} else {
int j;
@@ -249,8 +554,15 @@ sniff_unknown (SoupContentSniffer *sniffer, SoupMessage *msg,
}
}
- if (for_text_or_binary)
- return g_strdup ("application/octet-stream");
+ sniffed_type = sniff_images (sniffer, buffer);
+
+ if (sniffed_type != NULL)
+ return sniffed_type;
+
+ sniffed_type = sniff_audio_video (sniffer, buffer);
+
+ if (sniffed_type != NULL)
+ return sniffed_type;
for (i = 0; i < resource_length; i++) {
if (byte_looks_binary[resource[i]])
@@ -260,25 +572,29 @@ sniff_unknown (SoupContentSniffer *sniffer, SoupMessage *msg,
return g_strdup ("text/plain");
}
-/* HTML5: 2.7.3 Content-Type sniffing: text or binary */
+/* MIMESNIFF: 7.2 Sniffing a mislabeled binary resource */
static char*
-sniff_text_or_binary (SoupContentSniffer *sniffer, SoupMessage *msg,
- SoupBuffer *buffer)
+sniff_text_or_binary (SoupContentSniffer *sniffer, SoupBuffer *buffer)
{
const guchar *resource = (const guchar *)buffer->data;
int resource_length = MIN (512, buffer->length);
gboolean looks_binary = FALSE;
int i;
- /* Detecting UTF-16BE, UTF-16LE, or UTF-8 BOMs means it's text/plain */
- if (resource_length >= 4) {
+ /* 2. Detecting UTF-16BE, UTF-16LE BOMs means it's text/plain */
+ if (resource_length >= 2) {
if ((resource[0] == 0xFE && resource[1] == 0xFF) ||
- (resource[0] == 0xFF && resource[1] == 0xFE) ||
- (resource[0] == 0xEF && resource[1] == 0xBB && resource[2] == 0xBF))
+ (resource[0] == 0xFF && resource[1] == 0xFE))
+ return g_strdup ("text/plain");
+ }
+
+ /* 3. UTF-8 BOM. */
+ if (resource_length >= 3) {
+ if (resource[0] == 0xEF && resource[1] == 0xBB && resource[2] == 0xBF)
return g_strdup ("text/plain");
}
- /* Look to see if any of the first n bytes looks binary */
+ /* 4. Look to see if any of the first n bytes looks binary */
for (i = 0; i < resource_length; i++) {
if (byte_looks_binary[resource[i]]) {
looks_binary = TRUE;
@@ -289,40 +605,32 @@ sniff_text_or_binary (SoupContentSniffer *sniffer, SoupMessage *msg,
if (!looks_binary)
return g_strdup ("text/plain");
- return sniff_unknown (sniffer, msg, buffer, TRUE);
+ /* 5. Execute 7.1 Identifying a resource with an unknown MIME type.
+ * TODO: sniff-scriptable needs to be unset.
+ */
+ return sniff_unknown (sniffer, buffer, TRUE);
}
-static char*
-sniff_images (SoupContentSniffer *sniffer, SoupMessage *msg,
- SoupBuffer *buffer, const char *content_type)
+static gboolean
+skip_insignificant_space (const char *resource, int *pos, int resource_length)
{
- const guchar *resource = (const guchar *)buffer->data;
- int resource_length = MIN (512, buffer->length);
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (types_table); i++) {
- SoupContentSnifferPattern *type_row = &(types_table[i]);
-
- if (resource_length < type_row->pattern_length)
- continue;
-
- if (!g_str_has_prefix (type_row->sniffed_type, "image/"))
- continue;
-
- /* All of the image types use all-\xFF for the mask,
- * so we can just memcmp.
- */
- if (memcmp (type_row->pattern, resource, type_row->pattern_length) == 0)
- return g_strdup (type_row->sniffed_type);
+ while ((resource[*pos] == '\x09') ||
+ (resource[*pos] == '\x20') ||
+ (resource[*pos] == '\x0A') ||
+ (resource[*pos] == '\x0D')) {
+ *pos = *pos + 1;
+
+ if (*pos > resource_length)
+ return TRUE;
}
- return g_strdup (content_type);
+ return FALSE;
}
static char*
-sniff_feed_or_html (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer)
+sniff_feed_or_html (SoupContentSniffer *sniffer, SoupBuffer *buffer)
{
- const guchar *resource = (const guchar *)buffer->data;
+ const char *resource = (const char *)buffer->data;
int resource_length = MIN (512, buffer->length);
int pos = 0;
@@ -337,19 +645,10 @@ sniff_feed_or_html (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *b
if (pos > resource_length)
goto text_html;
- /* Skip insignificant white space */
- while ((resource[pos] == '\x09') ||
- (resource[pos] == '\x20') ||
- (resource[pos] == '\x0A') ||
- (resource[pos] == '\x0D')) {
- pos++;
-
- if (pos > resource_length)
- goto text_html;
- }
+ if (skip_insignificant_space (resource, &pos, resource_length))
+ goto text_html;
- /* != < */
- if (resource[pos] != '\x3C')
+ if (resource[pos] != '<')
return g_strdup ("text/html");
pos++;
@@ -357,73 +656,106 @@ sniff_feed_or_html (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *b
if ((pos + 2) > resource_length)
goto text_html;
- /* Skipping comments */
- if ((resource[pos] == '\x2D') ||
- (resource[pos+1] == '\x2D') ||
- (resource[pos+2] == '\x3E')) {
+ /* Skip comments. */
+ if (g_str_has_prefix (resource + pos, "!--")) {
pos = pos + 3;
if ((pos + 2) > resource_length)
goto text_html;
- while ((resource[pos] != '\x2D') &&
- (resource[pos+1] != '\x2D') &&
- (resource[pos+2] != '\x3E')) {
+ while (!g_str_has_prefix (resource + pos, "-->")) {
pos++;
if ((pos + 2) > resource_length)
goto text_html;
}
+ pos = pos + 3;
+
goto look_for_tag;
}
if (pos > resource_length)
goto text_html;
- /* == ! */
- if (resource[pos] == '\x21') {
+ if (resource[pos] == '!') {
do {
pos++;
if (pos > resource_length)
goto text_html;
- } while (resource[pos] != '\x3E');
+ } while (resource[pos] != '>');
pos++;
goto look_for_tag;
- } else if (resource[pos] == '\x3F') { /* ? */
+ } else if (resource[pos] == '?') {
do {
pos++;
if ((pos + 1) > resource_length)
goto text_html;
- } while ((resource[pos] != '\x3F') &&
- (resource[pos+1] != '\x3E'));
+ } while (!g_str_has_prefix (resource + pos, "?>"));
pos = pos + 2;
goto look_for_tag;
}
- if ((pos + 2) > resource_length)
+ if ((pos + 3) > resource_length)
goto text_html;
- if ((resource[pos] == '\x72') &&
- (resource[pos+1] == '\x73') &&
- (resource[pos+2] == '\x73'))
+ if (g_str_has_prefix (resource + pos, "rss"))
return g_strdup ("application/rss+xml");
- if ((pos + 3) > resource_length)
+ if ((pos + 4) > resource_length)
goto text_html;
- if ((resource[pos] == '\x66') &&
- (resource[pos+1] == '\x65') &&
- (resource[pos+2] == '\x65') &&
- (resource[pos+3] == '\x64'))
+ if (g_str_has_prefix (resource + pos, "feed"))
return g_strdup ("application/atom+xml");
+ if ((pos + 7) > resource_length)
+ goto text_html;
+
+ if (g_str_has_prefix (resource + pos, "rdf:RDF")) {
+ pos = pos + 7;
+
+ if (skip_insignificant_space (resource, &pos, resource_length))
+ goto text_html;
+
+ if ((pos + 32) > resource_length)
+ goto text_html;
+
+ if (g_str_has_prefix (resource + pos, "xmlns=\"http://purl.org/rss/1.0/\"")) {
+ pos = pos + 32;
+
+ if (skip_insignificant_space (resource, &pos, resource_length))
+ goto text_html;
+
+ if ((pos + 55) > resource_length)
+ goto text_html;
+
+ if (g_str_has_prefix (resource + pos, "xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""))
+ return g_strdup ("application/rss+xml");
+ }
+
+ if ((pos + 55) > resource_length)
+ goto text_html;
+
+ if (g_str_has_prefix (resource + pos, "xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"")) {
+ pos = pos + 55;
+
+ if (skip_insignificant_space (resource, &pos, resource_length))
+ goto text_html;
+
+ if ((pos + 32) > resource_length)
+ goto text_html;
+
+ if (g_str_has_prefix (resource + pos, "xmlns=\"http://purl.org/rss/1.0/\""))
+ return g_strdup ("application/rss+xml");
+ }
+ }
+
text_html:
return g_strdup ("text/html");
}
@@ -433,43 +765,71 @@ soup_content_sniffer_real_sniff (SoupContentSniffer *sniffer, SoupMessage *msg,
SoupBuffer *buffer, GHashTable **params)
{
const char *content_type;
+ const char *x_content_type_options;
+ char *sniffed_type = NULL;
+ gboolean no_sniff = FALSE;
content_type = soup_message_headers_get_content_type (msg->response_headers, params);
- /* These comparisons are done in an ASCII-case-insensitive
- * manner because the spec requires it */
+ /* MIMESNIFF: 7 Determining the sniffed MIME type of a resource. */
+
+ x_content_type_options = soup_message_headers_get_one (msg->response_headers, "X-Content-Type-Options");
+ if (!g_strcmp0 (x_content_type_options, "nosniff"))
+ no_sniff = TRUE;
+
+ /* 1. Unknown/undefined supplied type with sniff-scritable = !nosniff. */
if ((content_type == NULL) ||
!g_ascii_strcasecmp (content_type, "unknown/unknown") ||
!g_ascii_strcasecmp (content_type, "application/unknown") ||
!g_ascii_strcasecmp (content_type, "*/*"))
- return sniff_unknown (sniffer, msg, buffer, FALSE);
+ return sniff_unknown (sniffer, buffer, !no_sniff);
+
+ /* 2. If nosniff is specified in X-Content-Type-Options use the supplied MIME type. */
+ if (no_sniff)
+ return g_strdup (content_type);
+ /* 3. check-for-apache-bug */
+ if ((content_type != NULL) &&
+ (g_str_equal (content_type, "text/plain") ||
+ g_str_equal (content_type, "text/plain; charset=ISO-8859-1") ||
+ g_str_equal (content_type, "text/plain; charset=iso-8859-1") ||
+ g_str_equal (content_type, "text/plain; charset=UTF-8")))
+ return sniff_text_or_binary (sniffer, buffer);
+
+ /* 4. XML types sent by the server are always used. */
if (g_str_has_suffix (content_type, "+xml") ||
!g_ascii_strcasecmp (content_type, "text/xml") ||
!g_ascii_strcasecmp (content_type, "application/xml"))
return g_strdup (content_type);
- /* 2.7.5 Content-Type sniffing: image
- * The spec says:
- *
- * If the resource's official type is "image/svg+xml", then
- * the sniffed type of the resource is its official type (an
- * XML type)
- *
- * The XML case is handled by the if above; if you refactor
- * this code, keep this in mind.
+ /* 5. Distinguish feed from HTML. */
+ if (!g_ascii_strcasecmp (content_type, "text/html"))
+ return sniff_feed_or_html (sniffer, buffer);
+
+ /* 6. Image types.
*/
- if (!g_ascii_strncasecmp (content_type, "image/", 6))
- return sniff_images (sniffer, msg, buffer, content_type);
+ if (!g_ascii_strncasecmp (content_type, "image/", 6)) {
+ sniffed_type = sniff_images (sniffer, buffer);
+ if (sniffed_type != NULL)
+ return sniffed_type;
+ return g_strdup (content_type);
+ }
+
+ /* 7. Audio and video types. */
+ if (!g_ascii_strncasecmp (content_type, "audio/", 6) ||
+ !g_ascii_strncasecmp (content_type, "video/", 6) ||
+ !g_ascii_strcasecmp (content_type, "application/ogg")) {
+ sniffed_type = sniff_audio_video (sniffer, buffer);
+ if (sniffed_type != NULL)
+ return sniffed_type;
+ return g_strdup (content_type);
+ }
/* If we got text/plain, use text_or_binary */
if (g_str_equal (content_type, "text/plain")) {
- return sniff_text_or_binary (sniffer, msg, buffer);
+ return sniff_text_or_binary (sniffer, buffer);
}
- if (!g_ascii_strcasecmp (content_type, "text/html"))
- return sniff_feed_or_html (sniffer, msg, buffer);
-
return g_strdup (content_type);
}
@@ -535,7 +895,7 @@ soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_
*
* Returns: a new #SoupContentSniffer
*
- * Since: 2.27.3
+ * Since: 2.28
**/
SoupContentSniffer *
soup_content_sniffer_new ()
@@ -557,6 +917,8 @@ soup_content_sniffer_new ()
*
* Return value: the sniffed Content-Type of @buffer; this will never be %NULL,
* but may be "application/octet-stream".
+ *
+ * Since: 2.28
*/
char *
soup_content_sniffer_sniff (SoupContentSniffer *sniffer,
@@ -570,6 +932,17 @@ soup_content_sniffer_sniff (SoupContentSniffer *sniffer,
return SOUP_CONTENT_SNIFFER_GET_CLASS (sniffer)->sniff (sniffer, msg, buffer, params);
}
+/**
+ * soup_content_sniffer_get_buffer_size:
+ * @sniffer: a #SoupContentSniffer
+ *
+ * Gets the number of bytes @sniffer needs in order to properly sniff
+ * a buffer.
+ *
+ * Return value: the number of bytes to sniff
+ *
+ * Since: 2.28
+ */
gsize
soup_content_sniffer_get_buffer_size (SoupContentSniffer *sniffer)
{
diff --git a/libsoup/soup-content-sniffer.h b/libsoup/soup-content-sniffer.h
index 59c01546..eb1e4bd8 100644
--- a/libsoup/soup-content-sniffer.h
+++ b/libsoup/soup-content-sniffer.h
@@ -43,14 +43,18 @@ typedef struct {
void (*_libsoup_reserved5) (void);
} SoupContentSnifferClass;
+SOUP_AVAILABLE_IN_2_28
GType soup_content_sniffer_get_type (void);
+SOUP_AVAILABLE_IN_2_28
SoupContentSniffer *soup_content_sniffer_new (void);
+SOUP_AVAILABLE_IN_2_28
char *soup_content_sniffer_sniff (SoupContentSniffer *sniffer,
SoupMessage *msg,
SoupBuffer *buffer,
GHashTable **params);
+SOUP_AVAILABLE_IN_2_28
gsize soup_content_sniffer_get_buffer_size (SoupContentSniffer *sniffer);
G_END_DECLS
diff --git a/libsoup/soup-converter-wrapper.c b/libsoup/soup-converter-wrapper.c
index 8aa000b9..6fa19d91 100644
--- a/libsoup/soup-converter-wrapper.c
+++ b/libsoup/soup-converter-wrapper.c
@@ -15,12 +15,14 @@
#include "soup.h"
/* SoupConverterWrapper is a GConverter that wraps another GConverter.
- * Mostly it is transparent, but it implements three special fallbacks
+ * Mostly it is transparent, but it implements four special fallbacks
* for Content-Encoding handling: (1) "deflate" can mean either raw
- * deflate or zlib-encoded default, (2) the server may mistakenly
+ * deflate or zlib-encoded deflate, (2) the server may mistakenly
* claim that a response is encoded when actually it isn't, (3) the
* response may contain trailing junk after the end of the encoded
- * portion that we want to ignore.
+ * portion that we want to ignore, (4) the response may be truncated
+ * at an arbitrary point rather than containing a complete compressed
+ * representation.
*
* If the wrapped conversion succeeds, then the wrapper will set the
* %SOUP_MESSAGE_CONTENT_DECODED flag on its message.
@@ -48,11 +50,20 @@ struct _SoupConverterWrapperPrivate
};
static void
+soup_converter_wrapper_init (SoupConverterWrapper *converter)
+{
+ converter->priv = G_TYPE_INSTANCE_GET_PRIVATE (converter,
+ SOUP_TYPE_CONVERTER_WRAPPER,
+ SoupConverterWrapperPrivate);
+}
+
+static void
soup_converter_wrapper_finalize (GObject *object)
{
SoupConverterWrapperPrivate *priv = SOUP_CONVERTER_WRAPPER (object)->priv;
g_clear_object (&priv->base_converter);
+ g_clear_object (&priv->msg);
G_OBJECT_CLASS (soup_converter_wrapper_parent_class)->finalize (object);
}
@@ -114,14 +125,6 @@ soup_converter_wrapper_get_property (GObject *object,
}
static void
-soup_converter_wrapper_init (SoupConverterWrapper *converter)
-{
- converter->priv = G_TYPE_INSTANCE_GET_PRIVATE (converter,
- SOUP_TYPE_CONVERTER_WRAPPER,
- SoupConverterWrapperPrivate);
-}
-
-static void
soup_converter_wrapper_class_init (SoupConverterWrapperClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
@@ -271,8 +274,7 @@ soup_converter_wrapper_real_convert (GConverter *converter,
}
if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT) &&
- !priv->started && inbuf_size == 0 &&
- (flags & G_CONVERTER_INPUT_AT_END)) {
+ inbuf_size == 0 && (flags & G_CONVERTER_INPUT_AT_END)) {
/* Server claimed compression but there was no message body. */
g_error_free (my_error);
*bytes_written = 0;
diff --git a/libsoup/soup-cookie-jar-db.c b/libsoup/soup-cookie-jar-db.c
new file mode 100644
index 00000000..8f21baab
--- /dev/null
+++ b/libsoup/soup-cookie-jar-db.c
@@ -0,0 +1,337 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-cookie-jar-db.c: database-based cookie storage
+ *
+ * Using danw's soup-cookie-jar-text as template
+ * Copyright (C) 2008 Diego Escalante Urrelo
+ * Copyright (C) 2009 Collabora Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <sqlite3.h>
+
+#include "soup-cookie-jar-db.h"
+#include "soup.h"
+
+/**
+ * SECTION:soup-cookie-jar-db
+ * @short_description: Database-based Cookie Jar
+ *
+ * #SoupCookieJarDB is a #SoupCookieJar that reads cookies from and
+ * writes them to a sqlite database in the new Mozilla format.
+ *
+ * (This is identical to <literal>SoupCookieJarSqlite</literal> in
+ * libsoup-gnome; it has just been moved into libsoup proper, and
+ * renamed to avoid conflicting.)
+ **/
+
+enum {
+ PROP_0,
+
+ PROP_FILENAME,
+
+ LAST_PROP
+};
+
+typedef struct {
+ char *filename;
+ sqlite3 *db;
+} SoupCookieJarDBPrivate;
+
+#define SOUP_COOKIE_JAR_DB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_DB, SoupCookieJarDBPrivate))
+
+G_DEFINE_TYPE (SoupCookieJarDB, soup_cookie_jar_db, SOUP_TYPE_COOKIE_JAR)
+
+static void load (SoupCookieJar *jar);
+
+static void
+soup_cookie_jar_db_init (SoupCookieJarDB *db)
+{
+}
+
+static void
+soup_cookie_jar_db_finalize (GObject *object)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (object);
+
+ g_free (priv->filename);
+ g_clear_pointer (&priv->db, sqlite3_close);
+
+ G_OBJECT_CLASS (soup_cookie_jar_db_parent_class)->finalize (object);
+}
+
+static void
+soup_cookie_jar_db_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ priv->filename = g_value_dup_string (value);
+ load (SOUP_COOKIE_JAR (object));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_cookie_jar_db_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FILENAME:
+ g_value_set_string (value, priv->filename);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * soup_cookie_jar_db_new:
+ * @filename: the filename to read to/write from, or %NULL
+ * @read_only: %TRUE if @filename is read-only
+ *
+ * Creates a #SoupCookieJarDB.
+ *
+ * @filename will be read in at startup to create an initial set of
+ * cookies. If @read_only is %FALSE, then the non-session cookies will
+ * be written to @filename when the 'changed' signal is emitted from
+ * the jar. (If @read_only is %TRUE, then the cookie jar will only be
+ * used for this session, and changes made to it will be lost when the
+ * jar is destroyed.)
+ *
+ * Return value: the new #SoupCookieJar
+ *
+ * Since: 2.42
+ **/
+SoupCookieJar *
+soup_cookie_jar_db_new (const char *filename, gboolean read_only)
+{
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ return g_object_new (SOUP_TYPE_COOKIE_JAR_DB,
+ SOUP_COOKIE_JAR_DB_FILENAME, filename,
+ SOUP_COOKIE_JAR_READ_ONLY, read_only,
+ NULL);
+}
+
+#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"
+#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
+#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
+#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
+
+enum {
+ COL_ID,
+ COL_NAME,
+ COL_VALUE,
+ COL_HOST,
+ COL_PATH,
+ COL_EXPIRY,
+ COL_LAST_ACCESS,
+ COL_SECURE,
+ COL_HTTP_ONLY,
+ N_COL,
+};
+
+static int
+callback (void *data, int argc, char **argv, char **colname)
+{
+ SoupCookie *cookie = NULL;
+ SoupCookieJar *jar = SOUP_COOKIE_JAR (data);
+
+ char *name, *value, *host, *path;
+ gulong expire_time;
+ time_t now;
+ int max_age;
+ gboolean http_only = FALSE, secure = FALSE;
+
+ now = time (NULL);
+
+ name = argv[COL_NAME];
+ value = argv[COL_VALUE];
+ host = argv[COL_HOST];
+ path = argv[COL_PATH];
+ expire_time = strtoul (argv[COL_EXPIRY], NULL, 10);
+
+ if (now >= expire_time)
+ return 0;
+ max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
+
+ http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
+ secure = (g_strcmp0 (argv[COL_SECURE], "1") == 0);
+
+ cookie = soup_cookie_new (name, value, host, path, max_age);
+
+ if (secure)
+ soup_cookie_set_secure (cookie, TRUE);
+ if (http_only)
+ soup_cookie_set_http_only (cookie, TRUE);
+
+ soup_cookie_jar_add_cookie (jar, cookie);
+
+ return 0;
+}
+
+static void
+try_create_table (sqlite3 *db)
+{
+ char *error = NULL;
+
+ if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+}
+
+static void
+exec_query_with_try_create_table (sqlite3 *db,
+ const char *sql,
+ int (*callback)(void*,int,char**,char**),
+ void *argument)
+{
+ char *error = NULL;
+ gboolean try_create = TRUE;
+
+try_exec:
+ if (sqlite3_exec (db, sql, callback, argument, &error)) {
+ if (try_create) {
+ try_create = FALSE;
+ try_create_table (db);
+ sqlite3_free (error);
+ error = NULL;
+ goto try_exec;
+ } else {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+ }
+}
+
+/* Follows sqlite3 convention; returns TRUE on error */
+static gboolean
+open_db (SoupCookieJar *jar)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (jar);
+
+ char *error = NULL;
+
+ if (sqlite3_open (priv->filename, &priv->db)) {
+ sqlite3_close (priv->db);
+ priv->db = NULL;
+ g_warning ("Can't open %s", priv->filename);
+ return TRUE;
+ }
+
+ if (sqlite3_exec (priv->db, "PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;", NULL, NULL, &error)) {
+ g_warning ("Failed to execute query: %s", error);
+ sqlite3_free (error);
+ }
+
+ return FALSE;
+}
+
+static void
+load (SoupCookieJar *jar)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (jar);
+
+ if (priv->db == NULL) {
+ if (open_db (jar))
+ return;
+ }
+
+ exec_query_with_try_create_table (priv->db, QUERY_ALL, callback, jar);
+}
+
+static void
+soup_cookie_jar_db_changed (SoupCookieJar *jar,
+ SoupCookie *old_cookie,
+ SoupCookie *new_cookie)
+{
+ SoupCookieJarDBPrivate *priv =
+ SOUP_COOKIE_JAR_DB_GET_PRIVATE (jar);
+ char *query;
+
+ if (priv->db == NULL) {
+ if (open_db (jar))
+ return;
+ }
+
+ if (old_cookie) {
+ query = sqlite3_mprintf (QUERY_DELETE,
+ old_cookie->name,
+ old_cookie->domain);
+ exec_query_with_try_create_table (priv->db, query, NULL, NULL);
+ sqlite3_free (query);
+ }
+
+ if (new_cookie && new_cookie->expires) {
+ gulong expires;
+
+ expires = (gulong)soup_date_to_time_t (new_cookie->expires);
+ query = sqlite3_mprintf (QUERY_INSERT,
+ new_cookie->name,
+ new_cookie->value,
+ new_cookie->domain,
+ new_cookie->path,
+ expires,
+ new_cookie->secure,
+ new_cookie->http_only);
+ exec_query_with_try_create_table (priv->db, query, NULL, NULL);
+ sqlite3_free (query);
+ }
+}
+
+static gboolean
+soup_cookie_jar_db_is_persistent (SoupCookieJar *jar)
+{
+ return TRUE;
+}
+
+static void
+soup_cookie_jar_db_class_init (SoupCookieJarDBClass *db_class)
+{
+ SoupCookieJarClass *cookie_jar_class =
+ SOUP_COOKIE_JAR_CLASS (db_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (db_class);
+
+ g_type_class_add_private (db_class, sizeof (SoupCookieJarDBPrivate));
+
+ cookie_jar_class->is_persistent = soup_cookie_jar_db_is_persistent;
+ cookie_jar_class->changed = soup_cookie_jar_db_changed;
+
+ object_class->finalize = soup_cookie_jar_db_finalize;
+ object_class->set_property = soup_cookie_jar_db_set_property;
+ object_class->get_property = soup_cookie_jar_db_get_property;
+
+ /**
+ * SOUP_COOKIE_JAR_DB_FILENAME:
+ *
+ * Alias for the #SoupCookieJarDB:filename property. (The
+ * cookie-storage filename.)
+ **/
+ g_object_class_install_property (
+ object_class, PROP_FILENAME,
+ g_param_spec_string (SOUP_COOKIE_JAR_DB_FILENAME,
+ "Filename",
+ "Cookie-storage filename",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/libsoup/soup-cookie-jar-db.h b/libsoup/soup-cookie-jar-db.h
new file mode 100644
index 00000000..1a989015
--- /dev/null
+++ b/libsoup/soup-cookie-jar-db.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Diego Escalante Urrelo
+ */
+
+#ifndef SOUP_COOKIE_JAR_DB_H
+#define SOUP_COOKIE_JAR_DB_H 1
+
+#include <libsoup/soup-cookie-jar.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_COOKIE_JAR_DB (soup_cookie_jar_db_get_type ())
+#define SOUP_COOKIE_JAR_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_COOKIE_JAR_DB, SoupCookieJarDB))
+#define SOUP_COOKIE_JAR_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_COOKIE_JAR_DB, SoupCookieJarDBClass))
+#define SOUP_IS_COOKIE_JAR_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_DB))
+#define SOUP_IS_COOKIE_JAR_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_COOKIE_JAR_DB))
+#define SOUP_COOKIE_JAR_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_COOKIE_JAR_DB, SoupCookieJarDBClass))
+
+typedef struct {
+ SoupCookieJar parent;
+
+} SoupCookieJarDB;
+
+typedef struct {
+ SoupCookieJarClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupCookieJarDBClass;
+
+#define SOUP_COOKIE_JAR_DB_FILENAME "filename"
+
+SOUP_AVAILABLE_IN_2_42
+GType soup_cookie_jar_db_get_type (void);
+
+SOUP_AVAILABLE_IN_2_42
+SoupCookieJar *soup_cookie_jar_db_new (const char *filename,
+ gboolean read_only);
+
+G_END_DECLS
+
+#endif /* SOUP_COOKIE_JAR_DB_H */
diff --git a/libsoup/soup-cookie-jar-sqlite.c b/libsoup/soup-cookie-jar-sqlite.c
index 97e6a0e6..705e0478 100644
--- a/libsoup/soup-cookie-jar-sqlite.c
+++ b/libsoup/soup-cookie-jar-sqlite.c
@@ -1,10 +1,8 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * soup-cookie-jar-sqlite.c: ff sqlite-based cookie storage
+ * soup-cookie-jar-sqlite.c: deprecated version of sqlite-based cookie storage
*
- * Using danw's soup-cookie-jar-text as template
- * Copyright (C) 2008 Diego Escalante Urrelo
- * Copyright (C) 2009 Collabora Ltd.
+ * Copyright 2012 Red Hat, Inc.
*/
#ifdef HAVE_CONFIG_H
@@ -13,18 +11,11 @@
#include <stdlib.h>
-#include <sqlite3.h>
+/* Avoid deprecation warnings */
+#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_40
-#include "soup-cookie-jar-sqlite.h"
#include "soup.h"
-
-/**
- * SECTION:soup-cookie-jar-sqlite
- * @short_description: SQLite-based Cookie Jar
- *
- * #SoupCookieJarSqlite is a #SoupCookieJar that reads cookies from and
- * writes them to an SQLite file in the new Mozilla format.
- **/
+#include "soup-cookie-jar-sqlite.h"
enum {
PROP_0,
@@ -34,89 +25,13 @@ enum {
LAST_PROP
};
-typedef struct {
- char *filename;
- sqlite3 *db;
-} SoupCookieJarSqlitePrivate;
-
-#define SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqlitePrivate))
-
-G_DEFINE_TYPE (SoupCookieJarSqlite, soup_cookie_jar_sqlite, SOUP_TYPE_COOKIE_JAR)
-
-static void load (SoupCookieJar *jar);
+G_DEFINE_TYPE (SoupCookieJarSqlite, soup_cookie_jar_sqlite, SOUP_TYPE_COOKIE_JAR_DB)
static void
soup_cookie_jar_sqlite_init (SoupCookieJarSqlite *sqlite)
{
}
-static void
-soup_cookie_jar_sqlite_finalize (GObject *object)
-{
- SoupCookieJarSqlitePrivate *priv =
- SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
-
- g_free (priv->filename);
-
- if (priv->db)
- sqlite3_close (priv->db);
-
- G_OBJECT_CLASS (soup_cookie_jar_sqlite_parent_class)->finalize (object);
-}
-
-static void
-soup_cookie_jar_sqlite_set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- SoupCookieJarSqlitePrivate *priv =
- SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
-
- switch (prop_id) {
- case PROP_FILENAME:
- priv->filename = g_value_dup_string (value);
- load (SOUP_COOKIE_JAR (object));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-soup_cookie_jar_sqlite_get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- SoupCookieJarSqlitePrivate *priv =
- SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
-
- switch (prop_id) {
- case PROP_FILENAME:
- g_value_set_string (value, priv->filename);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-/**
- * soup_cookie_jar_sqlite_new:
- * @filename: the filename to read to/write from, or %NULL
- * @read_only: %TRUE if @filename is read-only
- *
- * Creates a #SoupCookieJarSqlite.
- *
- * @filename will be read in at startup to create an initial set of
- * cookies. If @read_only is %FALSE, then the non-session cookies will
- * be written to @filename when the 'changed' signal is emitted from
- * the jar. (If @read_only is %TRUE, then the cookie jar will only be
- * used for this session, and changes made to it will be lost when the
- * jar is destroyed.)
- *
- * Return value: the new #SoupCookieJar
- *
- * Since: 2.26
- **/
SoupCookieJar *
soup_cookie_jar_sqlite_new (const char *filename, gboolean read_only)
{
@@ -128,208 +43,7 @@ soup_cookie_jar_sqlite_new (const char *filename, gboolean read_only)
NULL);
}
-#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"
-#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
-#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
-#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
-
-enum {
- COL_ID,
- COL_NAME,
- COL_VALUE,
- COL_HOST,
- COL_PATH,
- COL_EXPIRY,
- COL_LAST_ACCESS,
- COL_SECURE,
- COL_HTTP_ONLY,
- N_COL,
-};
-
-static int
-callback (void *data, int argc, char **argv, char **colname)
-{
- SoupCookie *cookie = NULL;
- SoupCookieJar *jar = SOUP_COOKIE_JAR (data);
-
- char *name, *value, *host, *path;
- gulong expire_time;
- time_t now;
- int max_age;
- gboolean http_only = FALSE, secure = FALSE;
-
- now = time (NULL);
-
- name = argv[COL_NAME];
- value = argv[COL_VALUE];
- host = argv[COL_HOST];
- path = argv[COL_PATH];
- expire_time = strtoul (argv[COL_EXPIRY], NULL, 10);
-
- if (now >= expire_time)
- return 0;
- max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
-
- http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
- secure = (g_strcmp0 (argv[COL_SECURE], "1") == 0);
-
- cookie = soup_cookie_new (name, value, host, path, max_age);
-
- if (secure)
- soup_cookie_set_secure (cookie, TRUE);
- if (http_only)
- soup_cookie_set_http_only (cookie, TRUE);
-
- soup_cookie_jar_add_cookie (jar, cookie);
-
- return 0;
-}
-
-static void
-try_create_table (sqlite3 *db)
-{
- char *error = NULL;
-
- if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) {
- g_warning ("Failed to execute query: %s", error);
- sqlite3_free (error);
- }
-}
-
-static void
-exec_query_with_try_create_table (sqlite3 *db,
- const char *sql,
- int (*callback)(void*,int,char**,char**),
- void *argument)
-{
- char *error = NULL;
- gboolean try_create = TRUE;
-
-try_exec:
- if (sqlite3_exec (db, sql, callback, argument, &error)) {
- if (try_create) {
- try_create = FALSE;
- try_create_table (db);
- sqlite3_free (error);
- error = NULL;
- goto try_exec;
- } else {
- g_warning ("Failed to execute query: %s", error);
- sqlite3_free (error);
- }
- }
-}
-
-/* Follows sqlite3 convention; returns TRUE on error */
-static gboolean
-open_db (SoupCookieJar *jar)
-{
- SoupCookieJarSqlitePrivate *priv =
- SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
-
- char *error = NULL;
-
- if (sqlite3_open (priv->filename, &priv->db)) {
- sqlite3_close (priv->db);
- priv->db = NULL;
- g_warning ("Can't open %s", priv->filename);
- return TRUE;
- }
-
- if (sqlite3_exec (priv->db, "PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;", NULL, NULL, &error)) {
- g_warning ("Failed to execute query: %s", error);
- sqlite3_free (error);
- }
-
- return FALSE;
-}
-
-static void
-load (SoupCookieJar *jar)
-{
- SoupCookieJarSqlitePrivate *priv =
- SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
-
- if (priv->db == NULL) {
- if (open_db (jar))
- return;
- }
-
- exec_query_with_try_create_table (priv->db, QUERY_ALL, callback, jar);
-}
-
-static void
-soup_cookie_jar_sqlite_changed (SoupCookieJar *jar,
- SoupCookie *old_cookie,
- SoupCookie *new_cookie)
-{
- SoupCookieJarSqlitePrivate *priv =
- SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
- char *query;
-
- if (priv->db == NULL) {
- if (open_db (jar))
- return;
- }
-
- if (old_cookie) {
- query = sqlite3_mprintf (QUERY_DELETE,
- old_cookie->name,
- old_cookie->domain);
- exec_query_with_try_create_table (priv->db, query, NULL, NULL);
- sqlite3_free (query);
- }
-
- if (new_cookie && new_cookie->expires) {
- gulong expires;
-
- expires = (gulong)soup_date_to_time_t (new_cookie->expires);
- query = sqlite3_mprintf (QUERY_INSERT,
- new_cookie->name,
- new_cookie->value,
- new_cookie->domain,
- new_cookie->path,
- expires,
- new_cookie->secure,
- new_cookie->http_only);
- exec_query_with_try_create_table (priv->db, query, NULL, NULL);
- sqlite3_free (query);
- }
-}
-
-static gboolean
-soup_cookie_jar_sqlite_is_persistent (SoupCookieJar *jar)
-{
- return TRUE;
-}
-
static void
soup_cookie_jar_sqlite_class_init (SoupCookieJarSqliteClass *sqlite_class)
{
- SoupCookieJarClass *cookie_jar_class =
- SOUP_COOKIE_JAR_CLASS (sqlite_class);
- GObjectClass *object_class = G_OBJECT_CLASS (sqlite_class);
-
- g_type_class_add_private (sqlite_class, sizeof (SoupCookieJarSqlitePrivate));
-
- cookie_jar_class->is_persistent = soup_cookie_jar_sqlite_is_persistent;
- cookie_jar_class->changed = soup_cookie_jar_sqlite_changed;
-
- object_class->finalize = soup_cookie_jar_sqlite_finalize;
- object_class->set_property = soup_cookie_jar_sqlite_set_property;
- object_class->get_property = soup_cookie_jar_sqlite_get_property;
-
- /**
- * SOUP_COOKIE_JAR_SQLITE_FILENAME:
- *
- * Alias for the #SoupCookieJarSqlite:filename property. (The
- * cookie-storage filename.)
- **/
- g_object_class_install_property (
- object_class, PROP_FILENAME,
- g_param_spec_string (SOUP_COOKIE_JAR_SQLITE_FILENAME,
- "Filename",
- "Cookie-storage filename",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
diff --git a/libsoup/soup-cookie-jar-sqlite.h b/libsoup/soup-cookie-jar-sqlite.h
index 19dfbfa1..adc27868 100644
--- a/libsoup/soup-cookie-jar-sqlite.h
+++ b/libsoup/soup-cookie-jar-sqlite.h
@@ -6,7 +6,7 @@
#ifndef SOUP_COOKIE_JAR_SQLITE_H
#define SOUP_COOKIE_JAR_SQLITE_H 1
-#include <libsoup/soup-cookie-jar.h>
+#include <libsoup/soup-cookie-jar-db.h>
G_BEGIN_DECLS
@@ -18,24 +18,23 @@ G_BEGIN_DECLS
#define SOUP_COOKIE_JAR_SQLITE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqliteClass))
typedef struct {
- SoupCookieJar parent;
+ SoupCookieJarDB parent;
} SoupCookieJarSqlite;
typedef struct {
- SoupCookieJarClass parent_class;
+ SoupCookieJarDBClass parent_class;
- /* Padding for future expansion */
- void (*_libsoup_reserved1) (void);
- void (*_libsoup_reserved2) (void);
- void (*_libsoup_reserved3) (void);
- void (*_libsoup_reserved4) (void);
} SoupCookieJarSqliteClass;
#define SOUP_COOKIE_JAR_SQLITE_FILENAME "filename"
+SOUP_AVAILABLE_IN_2_26
+SOUP_DEPRECATED_IN_2_42_FOR(soup_cookie_jar_db_get_type)
GType soup_cookie_jar_sqlite_get_type (void);
+SOUP_AVAILABLE_IN_2_26
+SOUP_DEPRECATED_IN_2_42_FOR(soup_cookie_jar_db_new)
SoupCookieJar *soup_cookie_jar_sqlite_new (const char *filename,
gboolean read_only);
diff --git a/libsoup/soup-cookie-jar-text.h b/libsoup/soup-cookie-jar-text.h
index cc186dd1..83b8f280 100644
--- a/libsoup/soup-cookie-jar-text.h
+++ b/libsoup/soup-cookie-jar-text.h
@@ -34,8 +34,10 @@ typedef struct {
#define SOUP_COOKIE_JAR_TEXT_FILENAME "filename"
+SOUP_AVAILABLE_IN_2_26
GType soup_cookie_jar_text_get_type (void);
+SOUP_AVAILABLE_IN_2_26
SoupCookieJar *soup_cookie_jar_text_new (const char *filename,
gboolean read_only);
diff --git a/libsoup/soup-cookie-jar.c b/libsoup/soup-cookie-jar.c
index 433e7046..83229705 100644
--- a/libsoup/soup-cookie-jar.c
+++ b/libsoup/soup-cookie-jar.c
@@ -13,11 +13,10 @@
#include "soup-cookie-jar.h"
#include "soup.h"
-#include "soup-marshal.h"
/**
* SECTION:soup-cookie-jar
- * @short_description: Automatic cookie handling for #SoupSession
+ * @short_description: Automatic cookie handling for SoupSession
*
* A #SoupCookieJar stores #SoupCookie<!-- -->s and arrange for them
* to be sent with the appropriate #SoupMessage<!-- -->s.
@@ -175,7 +174,7 @@ soup_cookie_jar_class_init (SoupCookieJarClass *jar_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupCookieJarClass, changed),
NULL, NULL,
- _soup_marshal_NONE__BOXED_BOXED,
+ NULL,
G_TYPE_NONE, 2,
SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE,
SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE);
@@ -241,6 +240,10 @@ soup_cookie_jar_new (void)
* This function exists for backward compatibility, but does not do
* anything any more; cookie jars are saved automatically when they
* are changed.
+ *
+ * Since: 2.24
+ *
+ * Deprecated: This is a no-op.
*/
void
soup_cookie_jar_save (SoupCookieJar *jar)
@@ -432,7 +435,7 @@ soup_cookie_jar_get_cookie_list (SoupCookieJar *jar, SoupURI *uri, gboolean for_
/**
* soup_cookie_jar_add_cookie:
* @jar: a #SoupCookieJar
- * @cookie: a #SoupCookie
+ * @cookie: (transfer full): a #SoupCookie
*
* Adds @cookie to @jar, emitting the 'changed' signal if we are modifying
* an existing cookie or adding a valid new cookie ('valid' means
@@ -510,7 +513,7 @@ soup_cookie_jar_add_cookie (SoupCookieJar *jar, SoupCookie *cookie)
* soup_cookie_jar_add_cookie_with_first_party:
* @jar: a #SoupCookieJar
* @first_party: the URI for the main document
- * @cookie: a #SoupCookie
+ * @cookie: (transfer full): a #SoupCookie
*
* Adds @cookie to @jar, emitting the 'changed' signal if we are modifying
* an existing cookie or adding a valid new cookie ('valid' means
@@ -791,6 +794,9 @@ soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
* document. If no first party is set in a message when this policy is
* in effect, cookies will be assumed to be third party by default.
*
+ * The policy for accepting or rejecting cookies returned in
+ * responses.
+ *
* Since: 2.30
*/
diff --git a/libsoup/soup-cookie-jar.h b/libsoup/soup-cookie-jar.h
index 7ef05066..e77f8ab3 100644
--- a/libsoup/soup-cookie-jar.h
+++ b/libsoup/soup-cookie-jar.h
@@ -47,37 +47,53 @@ typedef enum {
SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
} SoupCookieJarAcceptPolicy;
+SOUP_AVAILABLE_IN_2_24
GType soup_cookie_jar_get_type (void);
+SOUP_AVAILABLE_IN_2_24
SoupCookieJar * soup_cookie_jar_new (void);
-#ifndef LIBSOUP_DISABLE_DEPRECATED
-void soup_cookie_jar_save (SoupCookieJar *jar);
-#endif
+SOUP_AVAILABLE_IN_2_24
char * soup_cookie_jar_get_cookies (SoupCookieJar *jar,
SoupURI *uri,
gboolean for_http);
+SOUP_AVAILABLE_IN_2_40
GSList * soup_cookie_jar_get_cookie_list (SoupCookieJar *jar,
SoupURI *uri,
gboolean for_http);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_jar_set_cookie (SoupCookieJar *jar,
SoupURI *uri,
const char *cookie);
+SOUP_AVAILABLE_IN_2_30
void soup_cookie_jar_set_cookie_with_first_party (SoupCookieJar *jar,
SoupURI *uri,
SoupURI *first_party,
const char *cookie);
+SOUP_AVAILABLE_IN_2_26
void soup_cookie_jar_add_cookie (SoupCookieJar *jar,
SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_40
void soup_cookie_jar_add_cookie_with_first_party (SoupCookieJar *jar,
SoupURI *first_party,
SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_26
void soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_26
GSList * soup_cookie_jar_all_cookies (SoupCookieJar *jar);
+SOUP_AVAILABLE_IN_2_30
void soup_cookie_jar_set_accept_policy (SoupCookieJar *jar,
SoupCookieJarAcceptPolicy policy);
+SOUP_AVAILABLE_IN_2_30
SoupCookieJarAcceptPolicy soup_cookie_jar_get_accept_policy (SoupCookieJar *jar);
+SOUP_AVAILABLE_IN_2_40
gboolean soup_cookie_jar_is_persistent (SoupCookieJar *jar);
+#ifndef SOUP_DISABLE_DEPRECATED
+SOUP_AVAILABLE_IN_2_24
+SOUP_DEPRECATED_IN_2_26
+void soup_cookie_jar_save (SoupCookieJar *jar);
+#endif
+
G_END_DECLS
#endif /* SOUP_COOKIE_JAR_H */
diff --git a/libsoup/soup-cookie.c b/libsoup/soup-cookie.c
index c553c52e..de5efa13 100755
--- a/libsoup/soup-cookie.c
+++ b/libsoup/soup-cookie.c
@@ -18,17 +18,10 @@
/**
* SECTION:soup-cookie
* @short_description: HTTP Cookies
- * @see_also: #SoupMessage
- *
- * #SoupCookie implements HTTP cookies, primarily as described by
- * <ulink
- * url="http://wp.netscape.com/newsref/std/cookie_spec.html">the
- * original Netscape cookie specification</ulink>, but with slight
- * modifications based on <ulink
- * url="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</ulink>, <ulink
- * url="http://msdn2.microsoft.com/en-us/library/ms533046.aspx">Microsoft's
- * HttpOnly extension attribute</ulink>, and observed real-world usage
- * (and, in particular, based on what Firefox does).
+ * @see_also: #SoupMessage, #SoupCookieJar
+ *
+ * #SoupCookie implements HTTP cookies, as described by <ulink
+ * url="http://tools.ietf.org/html/rfc6265.txt">RFC 6265</ulink>.
*
* To have a #SoupSession handle cookies for your appliction
* automatically, use a #SoupCookieJar.
@@ -58,10 +51,9 @@
* is a hostname and must match exactly.
*
* @expires will be non-%NULL if the cookie uses either the original
- * "expires" attribute, or the "max-age" attribute specified in RFC
- * 2109. If @expires is %NULL, it indicates that neither "expires" nor
- * "max-age" was specified, and the cookie expires at the end of the
- * session.
+ * "expires" attribute, or the newer "max-age" attribute. If @expires
+ * is %NULL, it indicates that neither "expires" nor "max-age" was
+ * specified, and the cookie expires at the end of the session.
*
* If @http_only is set, the cookie should not be exposed to untrusted
* code (eg, javascript), so as to minimize the danger posed by
@@ -358,6 +350,10 @@ cookie_new_internal (const char *name, const char *value,
* soup_cookie_set_secure() and soup_cookie_set_http_only() if you
* need to set those attributes on the returned cookie.)
*
+ * If @domain starts with ".", that indicates a domain (which matches
+ * the string after the ".", or any hostname that has @domain as a
+ * suffix). Otherwise, it is a hostname and must match exactly.
+ *
* @max_age is used to set the "expires" attribute on the cookie; pass
* -1 to not include the attribute (indicating that the cookie expires
* with the current session), 0 for an already-expired cookie, or a
@@ -1065,6 +1061,8 @@ soup_cookie_applies_to_uri (SoupCookie *cookie, SoupURI *uri)
* match. This may change in the future.
*
* Return value: whether the cookies are equal.
+ *
+ * Since: 2.24
*/
gboolean
soup_cookie_equal (SoupCookie *cookie1, SoupCookie *cookie2)
diff --git a/libsoup/soup-cookie.h b/libsoup/soup-cookie.h
index 3a3c3886..96242913 100644
--- a/libsoup/soup-cookie.h
+++ b/libsoup/soup-cookie.h
@@ -20,6 +20,7 @@ struct _SoupCookie {
gboolean http_only;
};
+SOUP_AVAILABLE_IN_2_24
GType soup_cookie_get_type (void);
#define SOUP_TYPE_COOKIE (soup_cookie_get_type())
@@ -28,61 +29,91 @@ GType soup_cookie_get_type (void);
#define SOUP_COOKIE_MAX_AGE_ONE_WEEK (SOUP_COOKIE_MAX_AGE_ONE_DAY * 7)
#define SOUP_COOKIE_MAX_AGE_ONE_YEAR (SOUP_COOKIE_MAX_AGE_ONE_DAY * 365.2422)
+SOUP_AVAILABLE_IN_2_24
SoupCookie *soup_cookie_new (const char *name,
const char *value,
const char *domain,
const char *path,
int max_age);
+SOUP_AVAILABLE_IN_2_24
SoupCookie *soup_cookie_parse (const char *header,
SoupURI *origin);
+SOUP_AVAILABLE_IN_2_24
SoupCookie *soup_cookie_copy (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_32
const char *soup_cookie_get_name (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_name (SoupCookie *cookie,
const char *name);
+SOUP_AVAILABLE_IN_2_32
const char *soup_cookie_get_value (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_value (SoupCookie *cookie,
const char *value);
+SOUP_AVAILABLE_IN_2_32
const char *soup_cookie_get_domain (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_domain (SoupCookie *cookie,
const char *domain);
+SOUP_AVAILABLE_IN_2_32
const char *soup_cookie_get_path (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_path (SoupCookie *cookie,
const char *path);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_max_age (SoupCookie *cookie,
int max_age);
+SOUP_AVAILABLE_IN_2_32
SoupDate *soup_cookie_get_expires (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_expires (SoupCookie *cookie,
SoupDate *expires);
+SOUP_AVAILABLE_IN_2_32
gboolean soup_cookie_get_secure (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_secure (SoupCookie *cookie,
gboolean secure);
+SOUP_AVAILABLE_IN_2_32
gboolean soup_cookie_get_http_only (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_http_only (SoupCookie *cookie,
gboolean http_only);
+SOUP_AVAILABLE_IN_2_24
char *soup_cookie_to_set_cookie_header (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
char *soup_cookie_to_cookie_header (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
gboolean soup_cookie_applies_to_uri (SoupCookie *cookie,
SoupURI *uri);
+SOUP_AVAILABLE_IN_2_24
gboolean soup_cookie_equal (SoupCookie *cookie1,
SoupCookie *cookie2);
+SOUP_AVAILABLE_IN_2_24
void soup_cookie_free (SoupCookie *cookie);
+SOUP_AVAILABLE_IN_2_24
GSList *soup_cookies_from_response (SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_24
GSList *soup_cookies_from_request (SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_24
void soup_cookies_to_response (GSList *cookies,
SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_24
void soup_cookies_to_request (GSList *cookies,
SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_24
void soup_cookies_free (GSList *cookies);
+SOUP_AVAILABLE_IN_2_24
char *soup_cookies_to_cookie_header (GSList *cookies);
+SOUP_AVAILABLE_IN_2_30
gboolean soup_cookie_domain_matches (SoupCookie *cookie,
const char *host);
diff --git a/libsoup/soup-date.c b/libsoup/soup-date.c
index 99730786..d3dcf6f4 100644
--- a/libsoup/soup-date.c
+++ b/libsoup/soup-date.c
@@ -188,7 +188,7 @@ soup_date_new (int year, int month, int day,
* offset_seconds is 0, returns the current time.
*
* If @offset_seconds would indicate a time not expressible as a
- * #time_t, the return value will be clamped into range.
+ * <type>time_t</type>, the return value will be clamped into range.
*
* Return value: a new #SoupDate
**/
@@ -525,7 +525,7 @@ soup_date_new_from_string (const char *date_string)
/**
* soup_date_new_from_time_t:
- * @when: a #time_t
+ * @when: a <type>time_t</type>
*
* Creates a #SoupDate corresponding to @when
*
@@ -680,13 +680,13 @@ soup_date_to_string (SoupDate *date, SoupDateFormat format)
* soup_date_to_time_t:
* @date: a #SoupDate
*
- * Converts @date to a %time_t.
+ * Converts @date to a <type>time_t</type>.
*
- * If @date is not representable as a %time_t, it will be clamped into
- * range. (In particular, some HTTP cookies have expiration dates
- * after "Y2.038k" (2038-01-19T03:14:07Z).)
+ * If @date is not representable as a <type>time_t</type>, it will be
+ * clamped into range. (In particular, some HTTP cookies have
+ * expiration dates after "Y2.038k" (2038-01-19T03:14:07Z).)
*
- * Return value: @date as a %time_t
+ * Return value: @date as a <type>time_t</type>
**/
time_t
soup_date_to_time_t (SoupDate *date)
@@ -897,6 +897,8 @@ soup_date_get_offset (SoupDate *date)
* @date: a #SoupDate
*
* Copies @date.
+ *
+ * Since: 2.24
**/
SoupDate *
soup_date_copy (SoupDate *date)
@@ -915,6 +917,8 @@ soup_date_copy (SoupDate *date)
* @date: a #SoupDate
*
* Frees @date.
+ *
+ * Since: 2.24
**/
void
soup_date_free (SoupDate *date)
diff --git a/libsoup/soup-date.h b/libsoup/soup-date.h
index 09246072..c6414c16 100644
--- a/libsoup/soup-date.h
+++ b/libsoup/soup-date.h
@@ -35,37 +35,56 @@ typedef enum {
SOUP_DATE_ISO8601_XMLRPC
} SoupDateFormat;
+SOUP_AVAILABLE_IN_2_24
GType soup_date_get_type (void);
#define SOUP_TYPE_DATE (soup_date_get_type ())
+SOUP_AVAILABLE_IN_2_24
SoupDate *soup_date_new (int year,
int month,
int day,
int hour,
int minute,
int second);
+SOUP_AVAILABLE_IN_2_24
SoupDate *soup_date_new_from_string (const char *date_string);
+SOUP_AVAILABLE_IN_2_24
SoupDate *soup_date_new_from_time_t (time_t when);
+SOUP_AVAILABLE_IN_2_24
SoupDate *soup_date_new_from_now (int offset_seconds);
+SOUP_AVAILABLE_IN_2_24
char *soup_date_to_string (SoupDate *date,
SoupDateFormat format);
+SOUP_AVAILABLE_IN_2_24
time_t soup_date_to_time_t (SoupDate *date);
+SOUP_AVAILABLE_IN_2_24
void soup_date_to_timeval (SoupDate *date,
GTimeVal *time);
+SOUP_AVAILABLE_IN_2_24
gboolean soup_date_is_past (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_year (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_month (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_day (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_hour (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_minute (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_second (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_utc (SoupDate *date);
+SOUP_AVAILABLE_IN_2_32
int soup_date_get_offset (SoupDate *date);
+SOUP_AVAILABLE_IN_2_24
SoupDate *soup_date_copy (SoupDate *date);
+SOUP_AVAILABLE_IN_2_24
void soup_date_free (SoupDate *date);
G_END_DECLS
diff --git a/libsoup/soup-filter-input-stream.c b/libsoup/soup-filter-input-stream.c
index 79159c38..8067b882 100644
--- a/libsoup/soup-filter-input-stream.c
+++ b/libsoup/soup-filter-input-stream.c
@@ -22,6 +22,8 @@
struct _SoupFilterInputStreamPrivate {
GByteArray *buf;
+ gboolean need_more;
+ gboolean in_read_until;
};
static void soup_filter_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
@@ -78,7 +80,10 @@ soup_filter_input_stream_read_fn (GInputStream *stream,
{
SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
- if (fstream->priv->buf) {
+ if (!fstream->priv->in_read_until)
+ fstream->priv->need_more = FALSE;
+
+ if (fstream->priv->buf && !fstream->priv->in_read_until) {
return read_from_buf (fstream, buffer, count);
} else {
return g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
@@ -92,7 +97,7 @@ soup_filter_input_stream_is_readable (GPollableInputStream *stream)
{
SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
- if (fstream->priv->buf)
+ if (fstream->priv->buf && !fstream->priv->need_more)
return TRUE;
else
return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (fstream)->base_stream));
@@ -106,7 +111,10 @@ soup_filter_input_stream_read_nonblocking (GPollableInputStream *stream,
{
SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
- if (fstream->priv->buf) {
+ if (!fstream->priv->in_read_until)
+ fstream->priv->need_more = FALSE;
+
+ if (fstream->priv->buf && !fstream->priv->in_read_until) {
return read_from_buf (fstream, buffer, count);
} else {
return g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
@@ -122,7 +130,7 @@ soup_filter_input_stream_create_source (GPollableInputStream *stream,
SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
GSource *base_source, *pollable_source;
- if (fstream->priv->buf)
+ if (fstream->priv->buf && !fstream->priv->need_more)
base_source = g_timeout_source_new (0);
else
base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (fstream)->base_stream), cancellable);
@@ -177,7 +185,7 @@ soup_filter_input_stream_read_line (SoupFilterInputStream *fstream,
{
return soup_filter_input_stream_read_until (fstream, buffer, length,
"\n", 1, blocking,
- got_line,
+ TRUE, got_line,
cancellable, error);
}
@@ -188,6 +196,7 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream,
const void *boundary,
gsize boundary_length,
gboolean blocking,
+ gboolean include_boundary,
gboolean *got_boundary,
GCancellable *cancellable,
GError **error)
@@ -195,11 +204,13 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream,
gssize nread;
guint8 *p, *buf, *end;
gboolean eof = FALSE;
+ GError *my_error = NULL;
g_return_val_if_fail (SOUP_IS_FILTER_INPUT_STREAM (fstream), -1);
- g_return_val_if_fail (boundary_length < length, -1);
+ g_return_val_if_fail (!include_boundary || (boundary_length < length), -1);
*got_boundary = FALSE;
+ fstream->priv->need_more = FALSE;
if (!fstream->priv->buf || fstream->priv->buf->len < boundary_length) {
guint prev_len;
@@ -211,10 +222,12 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream,
g_byte_array_set_size (fstream->priv->buf, length);
buf = fstream->priv->buf->data;
- nread = g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
+ fstream->priv->in_read_until = TRUE;
+ nread = g_pollable_stream_read (G_INPUT_STREAM (fstream),
buf + prev_len, length - prev_len,
blocking,
- cancellable, error);
+ cancellable, &my_error);
+ fstream->priv->in_read_until = FALSE;
if (nread <= 0) {
if (prev_len)
fstream->priv->buf->len = prev_len;
@@ -225,8 +238,17 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream,
if (nread == 0 && prev_len)
eof = TRUE;
- else
+ else {
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ fstream->priv->need_more = TRUE;
+ if (my_error)
+ g_propagate_error (error, my_error);
+
return nread;
+ }
+
+ if (my_error)
+ g_propagate_error (error, my_error);
} else
fstream->priv->buf->len = prev_len + nread;
} else
@@ -237,8 +259,10 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream,
if (!eof)
end -= boundary_length;
for (p = buf; p <= end; p++) {
- if (!memcmp (p, boundary, boundary_length)) {
- p += boundary_length;
+ if (*p == *(guint8*)boundary &&
+ !memcmp (p, boundary, boundary_length)) {
+ if (include_boundary)
+ p += boundary_length;
*got_boundary = TRUE;
break;
}
@@ -247,9 +271,10 @@ soup_filter_input_stream_read_until (SoupFilterInputStream *fstream,
if (!*got_boundary && fstream->priv->buf->len < length && !eof)
goto fill_buffer;
- /* Return everything up to 'p' (which is either just after the
- * boundary, @boundary_len - 1 bytes before the end of the
- * buffer, or end-of-file).
+ /* Return everything up to 'p' (which is either just after the boundary if
+ * include_boundary is TRUE, just before the boundary if include_boundary is
+ * FALSE, @boundary_len - 1 bytes before the end of the buffer, or end-of-
+ * file).
*/
return read_from_buf (fstream, buffer, p - buf);
}
diff --git a/libsoup/soup-filter-input-stream.h b/libsoup/soup-filter-input-stream.h
index b86a476f..276b60f7 100644
--- a/libsoup/soup-filter-input-stream.h
+++ b/libsoup/soup-filter-input-stream.h
@@ -47,6 +47,7 @@ gssize soup_filter_input_stream_read_until (SoupFilterInputStream *fstre
const void *boundary,
gsize boundary_len,
gboolean blocking,
+ gboolean include_boundary,
gboolean *got_boundary,
GCancellable *cancellable,
GError **error);
diff --git a/libsoup/soup-form.c b/libsoup/soup-form.c
index 5946daeb..4dee5647 100644
--- a/libsoup/soup-form.c
+++ b/libsoup/soup-form.c
@@ -218,7 +218,8 @@ append_form_encoded (GString *str, const char *in)
if (*s == ' ') {
g_string_append_c (str, '+');
s++;
- } else if (!g_ascii_isalnum (*s))
+ } else if (!g_ascii_isalnum (*s) && (*s != '-') && (*s != '_')
+ && (*s != '.'))
g_string_append_printf (str, "%%%02X", (int)*s++);
else
g_string_append_c (str, *s++);
diff --git a/libsoup/soup-form.h b/libsoup/soup-form.h
index f2be1bcf..21b8db1e 100644
--- a/libsoup/soup-form.h
+++ b/libsoup/soup-form.h
@@ -15,6 +15,7 @@ G_BEGIN_DECLS
#define SOUP_FORM_MIME_TYPE_MULTIPART "multipart/form-data"
GHashTable *soup_form_decode (const char *encoded_form);
+SOUP_AVAILABLE_IN_2_26
GHashTable *soup_form_decode_multipart (SoupMessage *msg,
const char *file_control_name,
char **filename,
@@ -28,7 +29,7 @@ char *soup_form_encode_datalist (GData **form_data_set);
char *soup_form_encode_valist (const char *first_field,
va_list args);
-#ifndef LIBSOUP_DISABLE_DEPRECATED
+#ifndef SOUP_DISABLE_DEPRECATED
/* Compatibility with libsoup 2.3.0 */
#define soup_form_decode_urlencoded soup_form_decode
#define soup_form_encode_urlencoded soup_form_encode_hash
@@ -45,6 +46,7 @@ SoupMessage *soup_form_request_new_from_hash (const char *method,
SoupMessage *soup_form_request_new_from_datalist (const char *method,
const char *uri,
GData **form_data_set);
+SOUP_AVAILABLE_IN_2_26
SoupMessage *soup_form_request_new_from_multipart (const char *uri,
SoupMultipart *multipart);
diff --git a/libsoup/soup-gnome-features.c b/libsoup/soup-gnome-features.c
index 798712a2..4b40f34c 100644
--- a/libsoup/soup-gnome-features.c
+++ b/libsoup/soup-gnome-features.c
@@ -11,42 +11,11 @@
#include "soup-gnome-features.h"
-/**
- * SOUP_TYPE_PROXY_RESOLVER_GNOME:
- *
- * This returns the #GType of a #SoupProxyURIResolver that can be used to
- * resolve HTTP proxies for GNOME applications. You can add this to
- * a session using soup_session_add_feature_by_type() or by using the
- * %SOUP_SESSION_ADD_FEATURE_BY_TYPE construct-time property.
- *
- * This feature is included in %SOUP_TYPE_GNOME_FEATURES_2_26, so if
- * you are using that feature, you do not need to include this feature
- * separately.
- *
- * Since: 2.26
- **/
-/* This is actually declared in soup-proxy-resolver-gnome now */
-
-/**
- * SOUP_TYPE_GNOME_FEATURES_2_26:
- *
- * This returns the #GType of a #SoupSessionFeature that automatically
- * adds all of the GNOME features defined for libsoup 2.26 (which is
- * just %SOUP_TYPE_PROXY_RESOLVER_GNOME).
- *
- * You can add this to a session using
- * soup_session_add_feature_by_type() or by using the
- * %SOUP_SESSION_ADD_FEATURE_BY_TYPE construct-time property.
- *
- * Since: 2.26
- **/
GType
soup_gnome_features_2_26_get_type (void)
{
- /* Eventually this needs to be a special SoupSessionFeature
- * class that registers other features. But for now we can
- * just do this:
- */
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
return SOUP_TYPE_PROXY_RESOLVER_GNOME;
+ G_GNUC_END_IGNORE_DEPRECATIONS;
}
diff --git a/libsoup/soup-gnome-features.h b/libsoup/soup-gnome-features.h
index 84d64597..18f37dea 100644
--- a/libsoup/soup-gnome-features.h
+++ b/libsoup/soup-gnome-features.h
@@ -10,18 +10,20 @@
G_BEGIN_DECLS
+SOUP_AVAILABLE_IN_2_26
+SOUP_DEPRECATED_IN_2_42_FOR(SoupSession:proxy-resolver)
GType soup_proxy_resolver_gnome_get_type (void);
#define SOUP_TYPE_PROXY_RESOLVER_GNOME (soup_proxy_resolver_gnome_get_type ())
+SOUP_AVAILABLE_IN_2_26
+SOUP_DEPRECATED_IN_2_42
GType soup_gnome_features_2_26_get_type (void);
#define SOUP_TYPE_GNOME_FEATURES_2_26 (soup_gnome_features_2_26_get_type ())
-#ifndef G_OS_WIN32
-#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
GType soup_password_manager_gnome_get_type (void);
#define SOUP_TYPE_PASSWORD_MANAGER_GNOME (soup_password_manager_gnome_get_type ())
-#endif /* LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY */
-#endif
G_END_DECLS
diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c
index ffa1e77a..9c4c8083 100644
--- a/libsoup/soup-headers.c
+++ b/libsoup/soup-headers.c
@@ -702,14 +702,11 @@ parse_param_list (const char *header, char delim)
char *item, *eq, *name_end, *value;
gboolean override;
- list = parse_list (header, delim);
- if (!list)
- return NULL;
-
params = g_hash_table_new_full (soup_str_case_hash,
soup_str_case_equal,
g_free, NULL);
+ list = parse_list (header, delim);
for (iter = list; iter; iter = iter->next) {
item = iter->data;
override = FALSE;
diff --git a/libsoup/soup-headers.h b/libsoup/soup-headers.h
index cc542c34..687ea41e 100644
--- a/libsoup/soup-headers.h
+++ b/libsoup/soup-headers.h
@@ -13,6 +13,7 @@ G_BEGIN_DECLS
/* HTTP Header Parsing */
+SOUP_AVAILABLE_IN_2_26
gboolean soup_headers_parse (const char *str,
int len,
SoupMessageHeaders *dest);
@@ -47,12 +48,15 @@ gboolean soup_header_contains (const char *header,
const char *token);
GHashTable *soup_header_parse_param_list (const char *header);
+SOUP_AVAILABLE_IN_2_24
GHashTable *soup_header_parse_semi_param_list (const char *header);
void soup_header_free_param_list (GHashTable *param_list);
+SOUP_AVAILABLE_IN_2_26
void soup_header_g_string_append_param (GString *string,
const char *name,
const char *value);
+SOUP_AVAILABLE_IN_2_30
void soup_header_g_string_append_param_quoted (GString *string,
const char *name,
const char *value);
diff --git a/libsoup/soup-io-stream.c b/libsoup/soup-io-stream.c
index 74c71580..ea5ddef1 100644
--- a/libsoup/soup-io-stream.c
+++ b/libsoup/soup-io-stream.c
@@ -103,6 +103,8 @@ soup_io_stream_finalize (GObject *object)
SoupIOStream *siostream = SOUP_IO_STREAM (object);
g_clear_object (&siostream->priv->base_iostream);
+ g_clear_object (&siostream->priv->istream);
+ g_clear_object (&siostream->priv->ostream);
G_OBJECT_CLASS (soup_io_stream_parent_class)->finalize (object);
}
diff --git a/libsoup/soup-logger.c b/libsoup/soup-logger.c
index 3183cced..34a55764 100644
--- a/libsoup/soup-logger.c
+++ b/libsoup/soup-logger.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2001-2004 Novell, Inc.
* Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2013 Igalia, S.L.
*/
#ifdef HAVE_CONFIG_H
@@ -52,7 +53,7 @@
* </screen></informalexample>
*
* The <literal>Soup-Debug-Timestamp</literal> line gives the time (as
- * a #time_t) when the request was sent, or the response fully
+ * a <type>time_t</type>) when the request was sent, or the response fully
* received.
*
* The <literal>Soup-Debug</literal> line gives further debugging
@@ -68,13 +69,21 @@
* the first byte of the request gets written to the network (from the
* #SoupSession::request_started signal), which means that if you have
* not made the complete request body available at that point, it will
- * not be logged. The response is logged just after the last byte of
- * the response body is read from the network (from the
- * #SoupMessage::got_body or #SoupMessage::got_informational signal),
- * which means that the #SoupMessage::got_headers signal, and anything
- * triggered off it (such as #SoupSession::authenticate) will be
- * emitted <emphasis>before</emphasis> the response headers are
- * actually logged.
+ * not be logged.
+ *
+ * The response is logged just after the last byte of the response
+ * body is read from the network (from the #SoupMessage::got_body or
+ * #SoupMessage::got_informational signal), which means that the
+ * #SoupMessage::got_headers signal, and anything triggered off it
+ * (such as #SoupSession::authenticate) will be emitted
+ * <emphasis>before</emphasis> the response headers are actually
+ * logged.
+ *
+ * If the response doesn't happen to trigger the
+ * #SoupMessage::got_body nor #SoupMessage::got_informational signals
+ * due to, for example, a cancellation before receiving the last byte
+ * of the response body, the response will still be logged on the
+ * event of the #SoupMessage::finished signal.
**/
static void soup_logger_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
@@ -365,6 +374,9 @@ soup_logger_detach (SoupLogger *logger,
soup_session_remove_feature (session, SOUP_SESSION_FEATURE (logger));
}
+static void soup_logger_print (SoupLogger *logger, SoupLoggerLogLevel level,
+ char direction, const char *format, ...) G_GNUC_PRINTF (4, 5);
+
static void
soup_logger_print (SoupLogger *logger, SoupLoggerLogLevel level,
char direction, const char *format, ...)
@@ -425,7 +437,7 @@ soup_logger_print_basic_auth (SoupLogger *logger, const char *value)
*p = '*';
}
soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
- "Authorization: Basic [%.*s]", len, decoded);
+ "Authorization: Basic [%.*s]", (int)len, decoded);
g_free (decoded);
}
@@ -481,10 +493,22 @@ print_request (SoupLogger *logger, SoupMessage *msg,
return;
if (!soup_message_headers_get_one (msg->request_headers, "Host")) {
+ char *uri_host;
+
+ if (strchr (uri->host, ':'))
+ uri_host = g_strdup_printf ("[%s]", uri->host);
+ else if (g_hostname_is_non_ascii (uri->host))
+ uri_host = g_hostname_to_ascii (uri->host);
+ else
+ uri_host = uri->host;
+
soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
- "Host: %s%c%u", uri->host,
+ "Host: %s%c%u", uri_host,
soup_uri_uses_default_port (uri) ? '\0' : ':',
uri->port);
+
+ if (uri_host != uri->host)
+ g_free (uri_host);
}
soup_message_headers_iter_init (&iter, msg->request_headers);
while (soup_message_headers_iter_next (&iter, &name, &value)) {
@@ -562,6 +586,20 @@ print_response (SoupLogger *logger, SoupMessage *msg)
}
static void
+finished (SoupMessage *msg, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ g_mutex_lock (&priv->lock);
+
+ print_response (logger, msg);
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
+
+ g_mutex_unlock (&priv->lock);
+}
+
+static void
got_informational (SoupMessage *msg, gpointer user_data)
{
SoupLogger *logger = user_data;
@@ -569,6 +607,7 @@ got_informational (SoupMessage *msg, gpointer user_data)
g_mutex_lock (&priv->lock);
+ g_signal_handlers_disconnect_by_func (msg, finished, logger);
print_response (logger, msg);
soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
@@ -603,6 +642,7 @@ got_body (SoupMessage *msg, gpointer user_data)
g_mutex_lock (&priv->lock);
+ g_signal_handlers_disconnect_by_func (msg, finished, logger);
print_response (logger, msg);
soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
@@ -622,6 +662,9 @@ soup_logger_request_queued (SoupSessionFeature *logger,
g_signal_connect (msg, "got-body",
G_CALLBACK (got_body),
logger);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (finished),
+ logger);
}
static void
@@ -665,6 +708,7 @@ soup_logger_request_unqueued (SoupSessionFeature *logger,
g_signal_handlers_disconnect_by_func (msg, got_informational, logger);
g_signal_handlers_disconnect_by_func (msg, got_body, logger);
+ g_signal_handlers_disconnect_by_func (msg, finished, logger);
}
static void
diff --git a/libsoup/soup-logger.h b/libsoup/soup-logger.h
index 8ada23d0..d84c765e 100644
--- a/libsoup/soup-logger.h
+++ b/libsoup/soup-logger.h
@@ -53,12 +53,12 @@ GType soup_logger_get_type (void);
SoupLogger *soup_logger_new (SoupLoggerLogLevel level,
int max_body_size);
-#ifndef LIBSOUP_DISABLE_DEPRECATED
-/* Use soup_session_add/remove_feature */
-G_DEPRECATED_FOR(soup_session_add_feature)
+
+#ifndef SOUP_DISABLE_DEPRECATED
+SOUP_DEPRECATED_IN_2_24_FOR(soup_session_add_feature)
void soup_logger_attach (SoupLogger *logger,
SoupSession *session);
-G_DEPRECATED_FOR(soup_session_remove_feature)
+SOUP_DEPRECATED_IN_2_24_FOR(soup_session_remove_feature)
void soup_logger_detach (SoupLogger *logger,
SoupSession *session);
#endif
diff --git a/libsoup/soup-message-body.c b/libsoup/soup-message-body.c
index 0868d841..dfc29fae 100644
--- a/libsoup/soup-message-body.c
+++ b/libsoup/soup-message-body.c
@@ -313,6 +313,10 @@ soup_buffer_free (SoupBuffer *buffer)
* soup_buffer_get_as_bytes:
* @buffer: a #SoupBuffer
*
+ * Creates a #GBytes pointing to the same memory as @buffer. The
+ * #GBytes will hold a reference on @buffer to ensure that it is not
+ * freed while the #GBytes is still valid.
+ *
* Returns: (transfer full): a new #GBytes which has the same content
* as the #SoupBuffer.
*
@@ -415,7 +419,7 @@ soup_message_body_new (void)
* be discarded, and you will be responsible for recreating the
* request body after the #SoupMessage::restarted signal is emitted.
*
- * Since: 2.4.1
+ * Since: 2.24
**/
void
soup_message_body_set_accumulate (SoupMessageBody *body,
@@ -435,7 +439,7 @@ soup_message_body_set_accumulate (SoupMessageBody *body,
*
* Return value: the accumulate flag for @body.
*
- * Since: 2.4.1
+ * Since: 2.24
**/
gboolean
soup_message_body_get_accumulate (SoupMessageBody *body)
@@ -664,7 +668,7 @@ soup_message_body_get_chunk (SoupMessageBody *body, goffset offset)
* This is a low-level method which you should not normally need to
* use.
*
- * Since: 2.4.1
+ * Since: 2.24
**/
void
soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk)
@@ -691,7 +695,7 @@ soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk)
* there are further restrictions on its proper use which are not
* documented here.
*
- * Since: 2.4.1
+ * Since: 2.24
**/
void
soup_message_body_wrote_chunk (SoupMessageBody *body, SoupBuffer *chunk)
diff --git a/libsoup/soup-message-body.h b/libsoup/soup-message-body.h
index ee840837..885cfd00 100644
--- a/libsoup/soup-message-body.h
+++ b/libsoup/soup-message-body.h
@@ -28,6 +28,7 @@ GType soup_buffer_get_type (void);
SoupBuffer *soup_buffer_new (SoupMemoryUse use,
gconstpointer data,
gsize length);
+SOUP_AVAILABLE_IN_2_32
SoupBuffer *soup_buffer_new_take (guchar *data,
gsize length);
SoupBuffer *soup_buffer_new_subbuffer (SoupBuffer *parent,
@@ -39,12 +40,15 @@ SoupBuffer *soup_buffer_new_with_owner (gconstpointer data,
gpointer owner,
GDestroyNotify owner_dnotify);
gpointer soup_buffer_get_owner (SoupBuffer *buffer);
+SOUP_AVAILABLE_IN_2_32
void soup_buffer_get_data (SoupBuffer *buffer,
const guint8 **data,
gsize *length);
+SOUP_AVAILABLE_IN_2_40
+GBytes *soup_buffer_get_as_bytes (SoupBuffer *buffer);
+
SoupBuffer *soup_buffer_copy (SoupBuffer *buffer);
void soup_buffer_free (SoupBuffer *buffer);
-GBytes *soup_buffer_get_as_bytes (SoupBuffer *buffer);
typedef struct {
const char *data;
@@ -56,14 +60,17 @@ GType soup_message_body_get_type (void);
SoupMessageBody *soup_message_body_new (void);
+SOUP_AVAILABLE_IN_2_24
void soup_message_body_set_accumulate(SoupMessageBody *body,
gboolean accumulate);
+SOUP_AVAILABLE_IN_2_24
gboolean soup_message_body_get_accumulate(SoupMessageBody *body);
void soup_message_body_append (SoupMessageBody *body,
SoupMemoryUse use,
gconstpointer data,
gsize length);
+SOUP_AVAILABLE_IN_2_32
void soup_message_body_append_take (SoupMessageBody *body,
guchar *data,
gsize length);
@@ -77,8 +84,10 @@ SoupBuffer *soup_message_body_flatten (SoupMessageBody *body);
SoupBuffer *soup_message_body_get_chunk (SoupMessageBody *body,
goffset offset);
+SOUP_AVAILABLE_IN_2_24
void soup_message_body_got_chunk (SoupMessageBody *body,
SoupBuffer *chunk);
+SOUP_AVAILABLE_IN_2_24
void soup_message_body_wrote_chunk (SoupMessageBody *body,
SoupBuffer *chunk);
diff --git a/libsoup/soup-message-client-io.c b/libsoup/soup-message-client-io.c
index 1310bb85..b145bbaf 100644
--- a/libsoup/soup-message-client-io.c
+++ b/libsoup/soup-message-client-io.c
@@ -11,6 +11,8 @@
#include <string.h>
+#include <glib/gi18n-lib.h>
+
#include "soup.h"
#include "soup-connection.h"
#include "soup-message-private.h"
@@ -18,67 +20,76 @@
#include "soup-misc-private.h"
static guint
-parse_response_headers (SoupMessage *req,
+parse_response_headers (SoupMessage *msg,
char *headers, guint headers_len,
SoupEncoding *encoding,
- gpointer user_data)
+ gpointer user_data,
+ GError **error)
{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupHTTPVersion version;
- g_free(req->reason_phrase);
- req->reason_phrase = NULL;
+ g_free(msg->reason_phrase);
+ msg->reason_phrase = NULL;
if (!soup_headers_parse_response (headers, headers_len,
- req->response_headers,
+ msg->response_headers,
&version,
- &req->status_code,
- &req->reason_phrase))
+ &msg->status_code,
+ &msg->reason_phrase)) {
+ g_set_error_literal (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_PARSING,
+ _("Could not parse HTTP response"));
return SOUP_STATUS_MALFORMED;
+ }
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
if (version < priv->http_version) {
priv->http_version = version;
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_HTTP_VERSION);
}
- if ((req->method == SOUP_METHOD_HEAD ||
- req->status_code == SOUP_STATUS_NO_CONTENT ||
- req->status_code == SOUP_STATUS_NOT_MODIFIED ||
- SOUP_STATUS_IS_INFORMATIONAL (req->status_code)) ||
- (req->method == SOUP_METHOD_CONNECT &&
- SOUP_STATUS_IS_SUCCESSFUL (req->status_code)))
+ if ((msg->method == SOUP_METHOD_HEAD ||
+ msg->status_code == SOUP_STATUS_NO_CONTENT ||
+ msg->status_code == SOUP_STATUS_NOT_MODIFIED ||
+ SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) ||
+ (msg->method == SOUP_METHOD_CONNECT &&
+ SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)))
*encoding = SOUP_ENCODING_NONE;
else
- *encoding = soup_message_headers_get_encoding (req->response_headers);
+ *encoding = soup_message_headers_get_encoding (msg->response_headers);
- if (*encoding == SOUP_ENCODING_UNRECOGNIZED)
+ if (*encoding == SOUP_ENCODING_UNRECOGNIZED) {
+ g_set_error_literal (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_ENCODING,
+ _("Unrecognized HTTP response encoding"));
return SOUP_STATUS_MALFORMED;
+ }
return SOUP_STATUS_OK;
}
static void
-get_request_headers (SoupMessage *req, GString *header,
+get_request_headers (SoupMessage *msg, GString *header,
SoupEncoding *encoding, gpointer user_data)
{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageQueueItem *item = user_data;
- SoupURI *uri = soup_message_get_uri (req);
+ SoupURI *uri = soup_message_get_uri (msg);
char *uri_host;
char *uri_string;
SoupMessageHeadersIter iter;
const char *name, *value;
if (strchr (uri->host, ':'))
- uri_host = g_strdup_printf ("[%s]", uri->host);
+ uri_host = g_strdup_printf ("[%.*s]", (int) strcspn (uri->host, "%"), uri->host);
else if (g_hostname_is_non_ascii (uri->host))
uri_host = g_hostname_to_ascii (uri->host);
else
uri_host = uri->host;
- if (req->method == SOUP_METHOD_CONNECT) {
+ if (msg->method == SOUP_METHOD_CONNECT) {
/* CONNECT URI is hostname:port for tunnel destination */
uri_string = g_strdup_printf ("%s:%d", uri_host, uri->port);
} else {
@@ -97,38 +108,35 @@ get_request_headers (SoupMessage *req, GString *header,
}
}
- if (priv->http_version == SOUP_HTTP_1_0) {
- g_string_append_printf (header, "%s %s HTTP/1.0\r\n",
- req->method, uri_string);
- } else {
- g_string_append_printf (header, "%s %s HTTP/1.1\r\n",
- req->method, uri_string);
- if (!soup_message_headers_get_one (req->request_headers, "Host")) {
- if (soup_uri_uses_default_port (uri)) {
- g_string_append_printf (header, "Host: %s\r\n",
- uri_host);
- } else {
- g_string_append_printf (header, "Host: %s:%d\r\n",
- uri_host, uri->port);
- }
+ g_string_append_printf (header, "%s %s HTTP/1.%d\r\n",
+ msg->method, uri_string,
+ (priv->http_version == SOUP_HTTP_1_0) ? 0 : 1);
+
+ if (!soup_message_headers_get_one (msg->request_headers, "Host")) {
+ if (soup_uri_uses_default_port (uri)) {
+ g_string_append_printf (header, "Host: %s\r\n",
+ uri_host);
+ } else {
+ g_string_append_printf (header, "Host: %s:%d\r\n",
+ uri_host, uri->port);
}
}
g_free (uri_string);
if (uri_host != uri->host)
g_free (uri_host);
- *encoding = soup_message_headers_get_encoding (req->request_headers);
+ *encoding = soup_message_headers_get_encoding (msg->request_headers);
if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH ||
*encoding == SOUP_ENCODING_NONE) &&
- (req->request_body->length > 0 ||
- soup_message_headers_get_one (req->request_headers, "Content-Type")) &&
- !soup_message_headers_get_content_length (req->request_headers)) {
+ (msg->request_body->length > 0 ||
+ soup_message_headers_get_one (msg->request_headers, "Content-Type")) &&
+ !soup_message_headers_get_content_length (msg->request_headers)) {
*encoding = SOUP_ENCODING_CONTENT_LENGTH;
- soup_message_headers_set_content_length (req->request_headers,
- req->request_body->length);
+ soup_message_headers_set_content_length (msg->request_headers,
+ msg->request_body->length);
}
- soup_message_headers_iter_init (&iter, req->request_headers);
+ soup_message_headers_iter_init (&iter, msg->request_headers);
while (soup_message_headers_iter_next (&iter, &name, &value))
g_string_append_printf (header, "%s: %s\r\n", name, value);
g_string_append (header, "\r\n");
@@ -142,7 +150,7 @@ soup_message_send_request (SoupMessageQueueItem *item,
GMainContext *async_context;
GIOStream *iostream;
- if (SOUP_IS_SESSION_ASYNC (item->session)) {
+ if (!SOUP_IS_SESSION_SYNC (item->session)) {
async_context = soup_session_get_async_context (item->session);
if (!async_context)
async_context = g_main_context_default ();
@@ -150,7 +158,6 @@ soup_message_send_request (SoupMessageQueueItem *item,
async_context = NULL;
iostream = soup_socket_get_iostream (soup_connection_get_socket (item->conn));
- soup_message_cleanup_response (item->msg);
soup_message_io_client (item, iostream, async_context,
get_request_headers,
parse_response_headers,
diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
index b9bab238..9c704b49 100644
--- a/libsoup/soup-message-headers.c
+++ b/libsoup/soup-message-headers.c
@@ -9,6 +9,7 @@
#include "soup-message-headers.h"
#include "soup.h"
+#include "soup-misc-private.h"
/**
* SECTION:soup-message-headers
@@ -300,7 +301,7 @@ soup_message_headers_remove (SoupMessageHeaders *hdrs, const char *name)
*
* Return value: the header's value or %NULL if not found.
*
- * Since: 2.26.1
+ * Since: 2.28
**/
const char *
soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
@@ -338,7 +339,7 @@ soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
*
* Return value: the header's value or %NULL if not found.
*
- * Since: 2.26.1
+ * Since: 2.28
**/
const char *
soup_message_headers_get_list (SoupMessageHeaders *hdrs, const char *name)
@@ -858,56 +859,40 @@ sort_ranges (gconstpointer a, gconstpointer b)
return ra->start - rb->start;
}
-/**
- * soup_message_headers_get_ranges:
- * @hdrs: a #SoupMessageHeaders
- * @total_length: the total_length of the response body
- * @ranges: (out): return location for an array of #SoupRange
- * @length: the length of the returned array
- *
- * Parses @hdrs's Range header and returns an array of the requested
- * byte ranges. The returned array must be freed with
- * soup_message_headers_free_ranges().
- *
- * If @total_length is non-0, its value will be used to adjust the
- * returned ranges to have explicit start and end values, and the
- * returned ranges will be sorted and non-overlapping. If
- * @total_length is 0, then some ranges may have an end value of -1,
- * as described under #SoupRange, and some of the ranges may be
- * redundant.
- *
- * Return value: %TRUE if @hdrs contained a "Range" header containing
- * byte ranges which could be parsed, %FALSE otherwise (in which case
- * @range and @length will not be set).
- *
- * Since: 2.26
- **/
-gboolean
-soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
- goffset total_length,
- SoupRange **ranges,
- int *length)
+/* like soup_message_headers_get_ranges(), except it returns:
+ * SOUP_STATUS_OK if there is no Range or it should be ignored.
+ * SOUP_STATUS_PARTIAL_CONTENT if there is at least one satisfiable range.
+ * SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE if @check_satisfiable
+ * is %TRUE and the request is not satisfiable given @total_length.
+ */
+guint
+soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs,
+ goffset total_length,
+ gboolean check_satisfiable,
+ SoupRange **ranges,
+ int *length)
{
const char *range = soup_message_headers_get_one (hdrs, "Range");
GSList *range_list, *r;
GArray *array;
char *spec, *end;
int i;
+ guint status = SOUP_STATUS_OK;
if (!range || strncmp (range, "bytes", 5) != 0)
- return FALSE;
+ return status;
range += 5;
while (g_ascii_isspace (*range))
range++;
if (*range++ != '=')
- return FALSE;
+ return status;
while (g_ascii_isspace (*range))
range++;
range_list = soup_header_parse_list (range);
if (!range_list)
- return FALSE;
+ return status;
array = g_array_new (FALSE, FALSE, sizeof (SoupRange));
for (r = range_list; r; r = r->next) {
@@ -921,22 +906,34 @@ soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
cur.start = g_ascii_strtoull (spec, &end, 10);
if (*end == '-')
end++;
- if (*end)
+ if (*end) {
cur.end = g_ascii_strtoull (end, &end, 10);
- else
+ if (cur.end < cur.start) {
+ status = SOUP_STATUS_OK;
+ break;
+ }
+ } else
cur.end = total_length - 1;
}
if (*end) {
- g_array_free (array, TRUE);
- soup_header_free_list (range_list);
- return FALSE;
+ status = SOUP_STATUS_OK;
+ break;
+ } else if (check_satisfiable && cur.start >= total_length) {
+ if (status == SOUP_STATUS_OK)
+ status = SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE;
+ continue;
}
g_array_append_val (array, cur);
+ status = SOUP_STATUS_PARTIAL_CONTENT;
}
-
soup_header_free_list (range_list);
+ if (status != SOUP_STATUS_PARTIAL_CONTENT) {
+ g_array_free (array, TRUE);
+ return status;
+ }
+
if (total_length) {
g_array_sort (array, sort_ranges);
for (i = 1; i < array->len; i++) {
@@ -954,7 +951,62 @@ soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
*length = array->len;
g_array_free (array, FALSE);
- return TRUE;
+ return SOUP_STATUS_PARTIAL_CONTENT;
+}
+
+/**
+ * soup_message_headers_get_ranges:
+ * @hdrs: a #SoupMessageHeaders
+ * @total_length: the total_length of the response body
+ * @ranges: (out): return location for an array of #SoupRange
+ * @length: the length of the returned array
+ *
+ * Parses @hdrs's Range header and returns an array of the requested
+ * byte ranges. The returned array must be freed with
+ * soup_message_headers_free_ranges().
+ *
+ * If @total_length is non-0, its value will be used to adjust the
+ * returned ranges to have explicit start and end values, and the
+ * returned ranges will be sorted and non-overlapping. If
+ * @total_length is 0, then some ranges may have an end value of -1,
+ * as described under #SoupRange, and some of the ranges may be
+ * redundant.
+ *
+ * Beware that even if given a @total_length, this function does not
+ * check that the ranges are satisfiable.
+ *
+ * <note><para>
+ * #SoupServer has built-in handling for range requests. If your
+ * server handler returns a %SOUP_STATUS_OK response containing the
+ * complete response body (rather than pausing the message and
+ * returning some of the response body later), and there is a Range
+ * header in the request, then libsoup will automatically convert the
+ * response to a %SOUP_STATUS_PARTIAL_CONTENT response containing only
+ * the range(s) requested by the client.
+ *
+ * The only time you need to process the Range header yourself is if
+ * either you need to stream the response body rather than returning
+ * it all at once, or you do not already have the complete response
+ * body available, and only want to generate the parts that were
+ * actually requested by the client.
+ * </para></note>
+ *
+ * Return value: %TRUE if @hdrs contained a syntactically-valid
+ * "Range" header, %FALSE otherwise (in which case @range and @length
+ * will not be set).
+ *
+ * Since: 2.26
+ **/
+gboolean
+soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
+ goffset total_length,
+ SoupRange **ranges,
+ int *length)
+{
+ guint status;
+
+ status = soup_message_headers_get_ranges_internal (hdrs, total_length, FALSE, ranges, length);
+ return status == SOUP_STATUS_PARTIAL_CONTENT;
}
/**
@@ -1104,6 +1156,12 @@ soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
* (Note that @total_length is the total length of the entire resource
* that this is a range of, not simply @end - @start + 1.)
*
+ * <note><para>
+ * #SoupServer has built-in handling for range requests, and you do
+ * not normally need to call this function youself. See
+ * soup_message_headers_get_ranges() for more details.
+ * </para></note>
+ *
* Since: 2.26
**/
void
@@ -1303,7 +1361,7 @@ soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs,
char *filename = strrchr (orig_value, '/');
if (filename)
- g_hash_table_insert (*params, orig_key, filename + 1);
+ g_hash_table_insert (*params, g_strdup (orig_key), filename + 1);
}
return TRUE;
}
diff --git a/libsoup/soup-message-headers.h b/libsoup/soup-message-headers.h
index da66f7a9..02e752bc 100644
--- a/libsoup/soup-message-headers.h
+++ b/libsoup/soup-message-headers.h
@@ -8,6 +8,8 @@
#include <libsoup/soup-types.h>
+G_BEGIN_DECLS
+
typedef struct SoupMessageHeaders SoupMessageHeaders;
GType soup_message_headers_get_type (void);
#define SOUP_TYPE_MESSAGE_HEADERS (soup_message_headers_get_type ())
@@ -33,15 +35,18 @@ void soup_message_headers_remove (SoupMessageHeaders *hdrs,
const char *name);
void soup_message_headers_clear (SoupMessageHeaders *hdrs);
+SOUP_AVAILABLE_IN_2_36
void soup_message_headers_clean_connection_headers (SoupMessageHeaders *hdrs);
-#ifndef LIBSOUP_DISABLE_DEPRECATED
-G_DEPRECATED_FOR(soup_message_headers_get_one or soup_message_headers_get_list)
+#ifndef SOUP_DISABLE_DEPRECATED
+SOUP_DEPRECATED_IN_2_28_FOR ("soup_message_headers_get_one or soup_message_headers_get_list")
const char *soup_message_headers_get (SoupMessageHeaders *hdrs,
const char *name);
#endif
+SOUP_AVAILABLE_IN_2_28
const char *soup_message_headers_get_one (SoupMessageHeaders *hdrs,
const char *name);
+SOUP_AVAILABLE_IN_2_28
const char *soup_message_headers_get_list (SoupMessageHeaders *hdrs,
const char *name);
@@ -97,40 +102,52 @@ typedef struct {
goffset end;
} SoupRange;
+SOUP_AVAILABLE_IN_2_26
gboolean soup_message_headers_get_ranges (SoupMessageHeaders *hdrs,
goffset total_length,
SoupRange **ranges,
int *length);
+SOUP_AVAILABLE_IN_2_26
void soup_message_headers_free_ranges (SoupMessageHeaders *hdrs,
SoupRange *ranges);
+SOUP_AVAILABLE_IN_2_26
void soup_message_headers_set_ranges (SoupMessageHeaders *hdrs,
SoupRange *ranges,
int length);
+SOUP_AVAILABLE_IN_2_26
void soup_message_headers_set_range (SoupMessageHeaders *hdrs,
goffset start,
goffset end);
+SOUP_AVAILABLE_IN_2_26
gboolean soup_message_headers_get_content_range (SoupMessageHeaders *hdrs,
goffset *start,
goffset *end,
goffset *total_length);
+SOUP_AVAILABLE_IN_2_26
void soup_message_headers_set_content_range (SoupMessageHeaders *hdrs,
goffset start,
goffset end,
goffset total_length);
+SOUP_AVAILABLE_IN_2_26
const char *soup_message_headers_get_content_type (SoupMessageHeaders *hdrs,
GHashTable **params);
+SOUP_AVAILABLE_IN_2_26
void soup_message_headers_set_content_type (SoupMessageHeaders *hdrs,
const char *content_type,
GHashTable *params);
+SOUP_AVAILABLE_IN_2_26
gboolean soup_message_headers_get_content_disposition (SoupMessageHeaders *hdrs,
char **disposition,
GHashTable **params);
+SOUP_AVAILABLE_IN_2_26
void soup_message_headers_set_content_disposition (SoupMessageHeaders *hdrs,
const char *disposition,
GHashTable *params);
+G_END_DECLS
+
#endif /* SOUP_MESSAGE_HEADERS_H */
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index eeb67553..be5cb2d2 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -16,8 +16,8 @@
#include "soup-body-output-stream.h"
#include "soup-client-input-stream.h"
#include "soup-connection.h"
+#include "soup-content-processor.h"
#include "soup-content-sniffer-stream.h"
-#include "soup-converter-wrapper.h"
#include "soup-filter-input-stream.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
@@ -60,7 +60,6 @@ typedef struct {
GOutputStream *ostream;
GOutputStream *body_ostream;
GMainContext *async_context;
- gboolean blocking;
SoupMessageIOState read_state;
SoupEncoding read_encoding;
@@ -156,8 +155,14 @@ soup_message_io_finished (SoupMessage *msg)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageIOData *io = priv->io_data;
- SoupMessageCompletionFn completion_cb = io->completion_cb;
- gpointer completion_data = io->completion_data;
+ SoupMessageCompletionFn completion_cb;
+ gpointer completion_data;
+
+ if (!io)
+ return;
+
+ completion_cb = io->completion_cb;
+ completion_data = io->completion_data;
g_object_ref (msg);
soup_message_io_cleanup (msg);
@@ -167,7 +172,8 @@ soup_message_io_finished (SoupMessage *msg)
}
static gboolean
-read_headers (SoupMessage *msg, GCancellable *cancellable, GError **error)
+read_headers (SoupMessage *msg, gboolean blocking,
+ GCancellable *cancellable, GError **error)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageIOData *io = priv->io_data;
@@ -180,7 +186,7 @@ read_headers (SoupMessage *msg, GCancellable *cancellable, GError **error)
nread = soup_filter_input_stream_read_line (io->istream,
io->read_header_buf->data + old_len,
RESPONSE_BLOCK_SIZE,
- io->blocking,
+ blocking,
&got_lf,
cancellable, error);
io->read_header_buf->len = old_len + MAX (nread, 0);
@@ -221,37 +227,53 @@ read_headers (SoupMessage *msg, GCancellable *cancellable, GError **error)
return TRUE;
}
-static void
-setup_body_istream (SoupMessage *msg)
+static gint
+processing_stage_cmp (gconstpointer a,
+ gconstpointer b)
{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
- SoupMessageIOData *io = priv->io_data;
- GConverter *decoder, *wrapper;
- GInputStream *filter;
- GSList *d;
-
- io->body_istream =
- soup_body_input_stream_new (io->istream,
- io->read_encoding,
- io->read_length);
-
- for (d = priv->decoders; d; d = d->next) {
- decoder = d->data;
- wrapper = soup_converter_wrapper_new (decoder, msg);
- filter = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
- "base-stream", io->body_istream,
- "converter", wrapper,
- NULL);
- g_object_unref (io->body_istream);
- io->body_istream = filter;
- }
+ SoupProcessingStage stage_a = soup_content_processor_get_processing_stage (SOUP_CONTENT_PROCESSOR (a));
+ SoupProcessingStage stage_b = soup_content_processor_get_processing_stage (SOUP_CONTENT_PROCESSOR (b));
+
+ if (stage_a > stage_b)
+ return 1;
+ if (stage_a == stage_b)
+ return 0;
+ return -1;
+}
- if (priv->sniffer) {
- filter = soup_content_sniffer_stream_new (priv->sniffer,
- msg, io->body_istream);
- g_object_unref (io->body_istream);
- io->body_istream = filter;
+GInputStream *
+soup_message_setup_body_istream (GInputStream *body_stream,
+ SoupMessage *msg,
+ SoupSession *session,
+ SoupProcessingStage start_at_stage)
+{
+ GInputStream *istream;
+ GSList *p, *processors;
+
+ istream = g_object_ref (body_stream);
+
+ processors = soup_session_get_features (session, SOUP_TYPE_CONTENT_PROCESSOR);
+ processors = g_slist_sort (processors, processing_stage_cmp);
+
+ for (p = processors; p; p = p->next) {
+ GInputStream *wrapper;
+ SoupContentProcessor *processor;
+
+ processor = SOUP_CONTENT_PROCESSOR (p->data);
+ if (soup_message_disables_feature (msg, p->data) ||
+ soup_content_processor_get_processing_stage (processor) < start_at_stage)
+ continue;
+
+ wrapper = soup_content_processor_wrap_input (processor, istream, msg, NULL);
+ if (wrapper) {
+ g_object_unref (istream);
+ istream = wrapper;
+ }
}
+
+ g_slist_free (processors);
+
+ return istream;
}
/*
@@ -287,7 +309,8 @@ setup_body_istream (SoupMessage *msg)
* socket not writable, write is complete, etc).
*/
static gboolean
-io_write (SoupMessage *msg, GCancellable *cancellable, GError **error)
+io_write (SoupMessage *msg, gboolean blocking,
+ GCancellable *cancellable, GError **error)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageIOData *io = priv->io_data;
@@ -306,7 +329,7 @@ io_write (SoupMessage *msg, GCancellable *cancellable, GError **error)
nwrote = g_pollable_stream_write (io->ostream,
io->write_buf->str + io->written,
io->write_buf->len - io->written,
- io->blocking,
+ blocking,
cancellable, error);
if (nwrote == -1)
return FALSE;
@@ -398,7 +421,7 @@ io_write (SoupMessage *msg, GCancellable *cancellable, GError **error)
nwrote = g_pollable_stream_write (io->body_ostream,
io->write_chunk->data + io->written,
io->write_chunk->length - io->written,
- io->blocking,
+ blocking,
cancellable, error);
if (nwrote == -1)
return FALSE;
@@ -470,7 +493,8 @@ io_write (SoupMessage *msg, GCancellable *cancellable, GError **error)
* socket not readable, read is complete, etc).
*/
static gboolean
-io_read (SoupMessage *msg, GCancellable *cancellable, GError **error)
+io_read (SoupMessage *msg, gboolean blocking,
+ GCancellable *cancellable, GError **error)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageIOData *io = priv->io_data;
@@ -481,13 +505,13 @@ io_read (SoupMessage *msg, GCancellable *cancellable, GError **error)
switch (io->read_state) {
case SOUP_MESSAGE_IO_STATE_HEADERS:
- if (!read_headers (msg, cancellable, error))
+ if (!read_headers (msg, blocking, cancellable, error))
return FALSE;
status = io->parse_headers_cb (msg, (char *)io->read_header_buf->data,
io->read_header_buf->len,
&io->read_encoding,
- io->header_data);
+ io->header_data, error);
g_byte_array_set_size (io->read_header_buf, 0);
if (status != SOUP_STATUS_OK) {
@@ -569,15 +593,31 @@ io_read (SoupMessage *msg, GCancellable *cancellable, GError **error)
case SOUP_MESSAGE_IO_STATE_BODY_START:
- if (!io->body_istream)
- setup_body_istream (msg);
+ if (!io->body_istream) {
+ GInputStream *body_istream = soup_body_input_stream_new (G_INPUT_STREAM (io->istream),
+ io->read_encoding,
+ io->read_length);
+
+ /* TODO: server-side messages do not have a io->item. This means
+ * that we cannot use content processors for them right now.
+ */
+ if (io->mode == SOUP_MESSAGE_IO_CLIENT) {
+ io->body_istream = soup_message_setup_body_istream (body_istream, msg,
+ io->item->session,
+ SOUP_STAGE_MESSAGE_BODY);
+ g_object_unref (body_istream);
+ } else {
+ io->body_istream = body_istream;
+ }
+ }
if (priv->sniffer) {
SoupContentSnifferStream *sniffer_stream = SOUP_CONTENT_SNIFFER_STREAM (io->body_istream);
const char *content_type;
GHashTable *params;
- if (!soup_content_sniffer_stream_is_ready (sniffer_stream, io->blocking, cancellable, error))
+ if (!soup_content_sniffer_stream_is_ready (sniffer_stream, blocking,
+ cancellable, error))
return FALSE;
content_type = soup_content_sniffer_stream_sniff (sniffer_stream, &params);
@@ -607,7 +647,7 @@ io_read (SoupMessage *msg, GCancellable *cancellable, GError **error)
nread = g_pollable_stream_read (io->body_istream,
(guchar *)buffer->data,
buffer->length,
- io->blocking,
+ blocking,
cancellable, error);
if (nread > 0) {
buffer->length = nread;
@@ -628,8 +668,6 @@ io_read (SoupMessage *msg, GCancellable *cancellable, GError **error)
case SOUP_MESSAGE_IO_STATE_BODY_DONE:
io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
- if (io->item && io->item->conn)
- soup_connection_set_reusable (io->item->conn);
soup_message_got_body (msg);
break;
@@ -800,7 +838,7 @@ request_is_restartable (SoupMessage *msg, GError *error)
}
static gboolean
-io_run_until (SoupMessage *msg,
+io_run_until (SoupMessage *msg, gboolean blocking,
SoupMessageIOState read_state, SoupMessageIOState write_state,
GCancellable *cancellable, GError **error)
{
@@ -824,9 +862,9 @@ io_run_until (SoupMessage *msg,
(io->read_state < read_state || io->write_state < write_state)) {
if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state))
- progress = io_read (msg, cancellable, &my_error);
+ progress = io_read (msg, blocking, cancellable, &my_error);
else if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->write_state))
- progress = io_write (msg, cancellable, &my_error);
+ progress = io_write (msg, blocking, cancellable, &my_error);
else
progress = FALSE;
}
@@ -858,7 +896,7 @@ io_run_until (SoupMessage *msg,
done = (io->read_state >= read_state &&
io->write_state >= write_state);
- if (io->paused && !done) {
+ if (!blocking && !done) {
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_WOULD_BLOCK,
_("Operation would block"));
@@ -870,8 +908,17 @@ io_run_until (SoupMessage *msg,
return done;
}
+static void io_run (SoupMessage *msg, gboolean blocking);
+
static gboolean
-io_run (SoupMessage *msg, gpointer user_data)
+io_run_ready (SoupMessage *msg, gpointer user_data)
+{
+ io_run (msg, FALSE);
+ return FALSE;
+}
+
+static void
+io_run (SoupMessage *msg, gboolean blocking)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageIOData *io = priv->io_data;
@@ -887,14 +934,14 @@ io_run (SoupMessage *msg, gpointer user_data)
g_object_ref (msg);
cancellable = io->cancellable ? g_object_ref (io->cancellable) : NULL;
- if (io_run_until (msg,
+ if (io_run_until (msg, blocking,
SOUP_MESSAGE_IO_STATE_DONE,
SOUP_MESSAGE_IO_STATE_DONE,
cancellable, &error)) {
soup_message_io_finished (msg);
} else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
g_clear_error (&error);
- io->io_source = soup_message_io_get_source (msg, NULL, io_run, msg);
+ io->io_source = soup_message_io_get_source (msg, NULL, io_run_ready, msg);
g_source_attach (io->io_source, io->async_context);
} else if (error && priv->io_data == io) {
if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN))
@@ -908,29 +955,28 @@ io_run (SoupMessage *msg, gpointer user_data)
g_error_free (error);
soup_message_io_finished (msg);
- }
+ } else if (error)
+ g_error_free (error);
g_object_unref (msg);
g_clear_object (&cancellable);
-
- return FALSE;
}
gboolean
-soup_message_io_run_until_write (SoupMessage *msg,
+soup_message_io_run_until_write (SoupMessage *msg, gboolean blocking,
GCancellable *cancellable, GError **error)
{
- return io_run_until (msg,
+ return io_run_until (msg, blocking,
SOUP_MESSAGE_IO_STATE_ANY,
SOUP_MESSAGE_IO_STATE_BODY,
cancellable, error);
}
gboolean
-soup_message_io_run_until_read (SoupMessage *msg,
+soup_message_io_run_until_read (SoupMessage *msg, gboolean blocking,
GCancellable *cancellable, GError **error)
{
- return io_run_until (msg,
+ return io_run_until (msg, blocking,
SOUP_MESSAGE_IO_STATE_BODY,
SOUP_MESSAGE_IO_STATE_ANY,
cancellable, error);
@@ -938,20 +984,30 @@ soup_message_io_run_until_read (SoupMessage *msg,
gboolean
soup_message_io_run_until_finish (SoupMessage *msg,
+ gboolean blocking,
GCancellable *cancellable,
GError **error)
{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ gboolean success;
+
g_object_ref (msg);
- if (!io_run_until (msg,
- SOUP_MESSAGE_IO_STATE_DONE,
- SOUP_MESSAGE_IO_STATE_DONE,
- cancellable, error))
- return FALSE;
+ if (io) {
+ g_return_val_if_fail (io->mode == SOUP_MESSAGE_IO_CLIENT, FALSE);
+
+ if (io->read_state < SOUP_MESSAGE_IO_STATE_BODY_DONE)
+ io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+ }
+
+ success = io_run_until (msg, blocking,
+ SOUP_MESSAGE_IO_STATE_DONE,
+ SOUP_MESSAGE_IO_STATE_DONE,
+ cancellable, error);
- soup_message_io_finished (msg);
g_object_unref (msg);
- return TRUE;
+ return success;
}
static void
@@ -1013,11 +1069,8 @@ new_iostate (SoupMessage *msg, GIOStream *iostream,
io->istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (iostream));
io->ostream = g_io_stream_get_output_stream (iostream);
- if (async_context) {
+ if (async_context)
io->async_context = g_main_context_ref (async_context);
- io->blocking = FALSE;
- } else
- io->blocking = TRUE;
io->read_header_buf = g_byte_array_new ();
io->write_buf = g_string_new (NULL);
@@ -1056,8 +1109,13 @@ soup_message_io_client (SoupMessageQueueItem *item,
io->write_body = item->msg->request_body;
io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
- if (!item->new_api)
- io_run (item->msg, NULL);
+
+ if (!item->new_api) {
+ gboolean blocking =
+ SOUP_IS_SESSION_SYNC (item->session) ||
+ (!SOUP_IS_SESSION_ASYNC (item->session) && !item->async);
+ io_run (item->msg, blocking);
+ }
}
void
@@ -1080,7 +1138,7 @@ soup_message_io_server (SoupMessage *msg,
io->write_body = msg->response_body;
io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
- io_run (msg, NULL);
+ io_run (msg, FALSE);
}
void
@@ -1102,6 +1160,7 @@ soup_message_io_pause (SoupMessage *msg)
if (io->unpause_source) {
g_source_destroy (io->unpause_source);
+ g_source_unref (io->unpause_source);
io->unpause_source = NULL;
}
@@ -1115,13 +1174,14 @@ io_unpause_internal (gpointer msg)
SoupMessageIOData *io = priv->io_data;
g_return_val_if_fail (io != NULL, FALSE);
- io->unpause_source = NULL;
+
+ g_clear_pointer (&io->unpause_source, g_source_unref);
io->paused = FALSE;
if (io->io_source)
return FALSE;
- io_run (msg, NULL);
+ io_run (msg, FALSE);
return FALSE;
}
@@ -1139,13 +1199,10 @@ soup_message_io_unpause (SoupMessage *msg)
return;
}
- if (!io->blocking) {
- if (!io->unpause_source) {
- io->unpause_source = soup_add_completion (
- io->async_context, io_unpause_internal, msg);
- }
- } else
- io_unpause_internal (msg);
+ if (!io->unpause_source) {
+ io->unpause_source = soup_add_completion_reffed (io->async_context,
+ io_unpause_internal, msg);
+ }
}
/**
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 69490e94..35cc9887 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -8,7 +8,9 @@
#include "soup-message.h"
#include "soup-auth.h"
+#include "soup-content-processor.h"
#include "soup-content-sniffer.h"
+#include "soup-session.h"
typedef struct {
gpointer io_data;
@@ -29,18 +31,22 @@ typedef struct {
SoupAddress *addr;
SoupAuth *auth, *proxy_auth;
+ SoupConnection *connection;
GSList *disabled_features;
- GSList *decoders;
SoupURI *first_party;
GTlsCertificate *tls_certificate;
GTlsCertificateFlags tls_errors;
+
+ SoupRequest *request;
+
+ SoupMessagePriority priority;
} SoupMessagePrivate;
#define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, SoupMessagePrivate))
-void soup_message_cleanup_response (SoupMessage *req);
+void soup_message_cleanup_response (SoupMessage *msg);
typedef void (*SoupMessageGetHeadersFn) (SoupMessage *msg,
@@ -51,7 +57,8 @@ typedef guint (*SoupMessageParseHeadersFn)(SoupMessage *msg,
char *headers,
guint header_len,
SoupEncoding *encoding,
- gpointer user_data);
+ gpointer user_data,
+ GError **error);
typedef void (*SoupMessageCompletionFn) (SoupMessage *msg,
gpointer user_data);
@@ -59,7 +66,7 @@ typedef void (*SoupMessageCompletionFn) (SoupMessage *msg,
void soup_message_send_request (SoupMessageQueueItem *item,
SoupMessageCompletionFn completion_cb,
gpointer user_data);
-void soup_message_read_request (SoupMessage *req,
+void soup_message_read_request (SoupMessage *msg,
SoupSocket *sock,
SoupMessageCompletionFn completion_cb,
gpointer user_data);
@@ -98,12 +105,15 @@ void soup_message_io_unpause (SoupMessage *msg);
gboolean soup_message_io_in_progress (SoupMessage *msg);
gboolean soup_message_io_run_until_write (SoupMessage *msg,
+ gboolean blocking,
GCancellable *cancellable,
GError **error);
gboolean soup_message_io_run_until_read (SoupMessage *msg,
+ gboolean blocking,
GCancellable *cancellable,
GError **error);
gboolean soup_message_io_run_until_finish (SoupMessage *msg,
+ gboolean blocking,
GCancellable *cancellable,
GError **error);
@@ -126,4 +136,16 @@ void soup_message_network_event (SoupMessage *msg,
GSocketClientEvent event,
GIOStream *connection);
+GInputStream *soup_message_setup_body_istream (GInputStream *body_stream,
+ SoupMessage *msg,
+ SoupSession *session,
+ SoupProcessingStage start_at_stage);
+
+void soup_message_set_soup_request (SoupMessage *msg,
+ SoupRequest *req);
+
+SoupConnection *soup_message_get_connection (SoupMessage *msg);
+void soup_message_set_connection (SoupMessage *msg,
+ SoupConnection *conn);
+
#endif /* SOUP_MESSAGE_PRIVATE_H */
diff --git a/libsoup/soup-message-queue.c b/libsoup/soup-message-queue.c
index 8ef129a9..4bc37267 100644
--- a/libsoup/soup-message-queue.c
+++ b/libsoup/soup-message-queue.c
@@ -58,28 +58,7 @@ queue_message_restarted (SoupMessage *msg, gpointer user_data)
{
SoupMessageQueueItem *item = user_data;
- if (item->proxy_addr) {
- g_object_unref (item->proxy_addr);
- item->proxy_addr = NULL;
- }
- if (item->proxy_uri) {
- soup_uri_free (item->proxy_uri);
- item->proxy_uri = NULL;
- }
-
- if (item->conn &&
- (!soup_message_is_keepalive (msg) ||
- SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
- if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
- soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
- soup_message_queue_item_set_connection (item, NULL);
- }
-
- soup_message_cleanup_response (msg);
-
g_cancellable_reset (item->cancellable);
-
- item->state = SOUP_MESSAGE_STARTING;
}
/**
@@ -101,13 +80,16 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
SoupMessageQueueItem *item;
item = g_slice_new0 (SoupMessageQueueItem);
- item->session = queue->session;
+ item->session = g_object_ref (queue->session);
item->async_context = soup_session_get_async_context (item->session);
+ if (item->async_context)
+ g_main_context_ref (item->async_context);
item->queue = queue;
item->msg = g_object_ref (msg);
item->callback = callback;
item->callback_data = user_data;
item->cancellable = g_cancellable_new ();
+ item->priority = soup_message_get_priority (msg);
g_signal_connect (msg, "restarted",
G_CALLBACK (queue_message_restarted), item);
@@ -120,9 +102,27 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
g_mutex_lock (&queue->mutex);
if (queue->head) {
- queue->tail->next = item;
- item->prev = queue->tail;
- queue->tail = item;
+ SoupMessageQueueItem *it = queue->head;
+
+ while (it && it->priority >= item->priority)
+ it = it->next;
+
+ if (!it) {
+ if (queue->tail) {
+ queue->tail->next = item;
+ item->prev = queue->tail;
+ } else
+ queue->head = item;
+ queue->tail = item;
+ } else {
+ if (it != queue->head)
+ it->prev->next = item;
+ else
+ queue->head = item;
+ item->prev = it->prev;
+ it->prev = item;
+ item->next = it;
+ }
} else
queue->head = queue->tail = item;
@@ -163,6 +163,8 @@ soup_message_queue_item_unref (SoupMessageQueueItem *item)
return;
}
+ g_warn_if_fail (item->conn == NULL);
+
/* OK, @item is dead. Rewrite @queue around it */
if (item->prev)
item->prev->next = item->next;
@@ -178,45 +180,17 @@ soup_message_queue_item_unref (SoupMessageQueueItem *item)
/* And free it */
g_signal_handlers_disconnect_by_func (item->msg,
queue_message_restarted, item);
+ g_object_unref (item->session);
g_object_unref (item->msg);
g_object_unref (item->cancellable);
- if (item->proxy_addr)
- g_object_unref (item->proxy_addr);
- if (item->proxy_uri)
- soup_uri_free (item->proxy_uri);
- if (item->result)
- g_object_unref (item->result);
- soup_message_queue_item_set_connection (item, NULL);
- g_slice_free (SoupMessageQueueItem, item);
-}
-
-static void
-proxy_connection_event (SoupConnection *conn,
- GSocketClientEvent event,
- GIOStream *connection,
- gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- soup_message_network_event (item->msg, event, connection);
-}
-
-void
-soup_message_queue_item_set_connection (SoupMessageQueueItem *item,
- SoupConnection *conn)
-{
- if (item->conn) {
- g_signal_handlers_disconnect_by_func (item->conn, proxy_connection_event, item);
- g_object_unref (item->conn);
- }
-
- item->conn = conn;
-
- if (item->conn) {
- g_object_ref (item->conn);
- g_signal_connect (item->conn, "event",
- G_CALLBACK (proxy_connection_event), item);
+ g_clear_error (&item->error);
+ g_clear_object (&item->task);
+ g_clear_pointer (&item->async_context, g_main_context_unref);
+ if (item->io_source) {
+ g_source_destroy (item->io_source);
+ g_source_unref (item->io_source);
}
+ g_slice_free (SoupMessageQueueItem, item);
}
/**
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
index 54c2971f..f86a1296 100644
--- a/libsoup/soup-message-queue.h
+++ b/libsoup/soup-message-queue.h
@@ -15,7 +15,6 @@ G_BEGIN_DECLS
typedef enum {
SOUP_MESSAGE_STARTING,
- SOUP_MESSAGE_AWAITING_CONNECTION,
SOUP_MESSAGE_GOT_CONNECTION,
SOUP_MESSAGE_CONNECTING,
SOUP_MESSAGE_CONNECTED,
@@ -23,6 +22,7 @@ typedef enum {
SOUP_MESSAGE_TUNNELED,
SOUP_MESSAGE_READY,
SOUP_MESSAGE_RUNNING,
+ SOUP_MESSAGE_CACHED,
SOUP_MESSAGE_RESTARTING,
SOUP_MESSAGE_FINISHING,
SOUP_MESSAGE_FINISHED
@@ -38,21 +38,24 @@ struct _SoupMessageQueueItem {
GMainContext *async_context;
GCancellable *cancellable;
- SoupAddress *proxy_addr;
- SoupURI *proxy_uri;
+ GError *error;
+
SoupConnection *conn;
- GSimpleAsyncResult *result;
+ GTask *task;
+ GSource *io_source;
guint paused : 1;
guint new_api : 1;
guint io_started : 1;
- guint redirection_count : 29;
+ guint async : 1;
+ guint resend_count : 28;
SoupMessageQueueItemState state;
/*< private >*/
guint removed : 1;
- guint ref_count : 31;
+ guint priority : 3;
+ guint ref_count : 28;
SoupMessageQueueItem *prev, *next;
SoupMessageQueueItem *related;
};
@@ -77,8 +80,6 @@ void soup_message_queue_destroy (SoupMessageQueue *queue
void soup_message_queue_item_ref (SoupMessageQueueItem *item);
void soup_message_queue_item_unref (SoupMessageQueueItem *item);
-void soup_message_queue_item_set_connection (SoupMessageQueueItem *item,
- SoupConnection *conn);
G_END_DECLS
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index a53e5b7b..2647b811 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -11,13 +11,15 @@
#include <string.h>
+#include <glib/gi18n-lib.h>
+
#include "soup.h"
#include "soup-message-private.h"
#include "soup-misc-private.h"
static guint
parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
- SoupEncoding *encoding, gpointer sock)
+ SoupEncoding *encoding, gpointer sock, GError **error)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
char *req_method, *req_path, *url;
@@ -31,8 +33,14 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
&req_method,
&req_path,
&version);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if (status == SOUP_STATUS_MALFORMED) {
+ g_set_error_literal (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_PARSING,
+ _("Could not parse HTTP request"));
+ }
return status;
+ }
g_object_set (G_OBJECT (msg),
SOUP_MESSAGE_METHOD, req_method,
@@ -90,10 +98,7 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
g_free (req_path);
- if (!SOUP_URI_VALID_FOR_HTTP (uri)) {
- /* certainly not "a valid host on the server" (RFC2616 5.2.3)
- * SOUP_URI_VALID_FOR_HTTP also guards against uri == NULL
- */
+ if (!uri || !uri->host) {
if (uri)
soup_uri_free (uri);
return SOUP_STATUS_BAD_REQUEST;
@@ -111,6 +116,7 @@ handle_partial_get (SoupMessage *msg)
SoupRange *ranges;
int nranges;
SoupBuffer *full_response;
+ guint status;
/* Make sure the message is set up right for us to return a
* partial response; it has to be a GET, the status must be
@@ -129,9 +135,15 @@ handle_partial_get (SoupMessage *msg)
/* Oh, and there has to have been a valid Range header on the
* request, of course.
*/
- if (!soup_message_headers_get_ranges (msg->request_headers,
- msg->response_body->length,
- &ranges, &nranges))
+ status = soup_message_headers_get_ranges_internal (msg->request_headers,
+ msg->response_body->length,
+ TRUE,
+ &ranges, &nranges);
+ if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
+ soup_message_set_status (msg, status);
+ soup_message_body_truncate (msg->response_body);
+ return;
+ } else if (status != SOUP_STATUS_PARTIAL_CONTENT)
return;
full_response = soup_message_body_flatten (msg->response_body);
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 3e1112e1..23fd8b08 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -5,13 +5,11 @@
* Copyright (C) 2000-2003, Ximian, Inc.
*/
-//#include <stdlib.h>
-//#include <string.h>
+#include <string.h>
#include "soup-message.h"
#include "soup.h"
#include "soup-connection.h"
-#include "soup-marshal.h"
#include "soup-message-private.h"
/**
@@ -22,9 +20,16 @@
* A #SoupMessage represents an HTTP message that is being sent or
* received.
*
- * For client-side usage, you would create a #SoupMessage with
+ * For client-side usage, if you are using the traditional
+ * #SoupSession APIs (soup_session_queue_message() and
+ * soup_session_send_message()), you would create a #SoupMessage with
* soup_message_new() or soup_message_new_from_uri(), set up its
- * fields appropriate, and send it via a #SoupSession.
+ * fields appropriately, and send it. If you are using the newer
+ * #SoupRequest API, you would create a request with
+ * soup_session_request_http() or soup_session_request_http_uri(), and
+ * the returned #SoupRequestHTTP will already have an associated
+ * #SoupMessage that you can retrieve via
+ * soup_request_http_get_message().
*
* For server-side usage, #SoupServer will create #SoupMessage<!--
* -->s automatically for incoming requests, which your application
@@ -49,7 +54,7 @@
*
* Represents an HTTP message being sent or received.
*
- * @status_code will normally be a #SoupKnownStatusCode, eg,
+ * @status_code will normally be a #SoupStatus value, eg,
* %SOUP_STATUS_OK, though of course it might actually be an unknown
* status code. @reason_phrase is the actual text returned from the
* server, which may or may not correspond to the "standard"
@@ -62,17 +67,22 @@
*
* As described in the #SoupMessageBody documentation, the
* @request_body and @response_body <literal>data</literal> fields
- * will not necessarily be filled in at all times. When they are
- * filled in, they will be terminated with a '\0' byte (which is not
- * included in the <literal>length</literal>), so you can use them as
- * ordinary C strings (assuming that you know that the body doesn't
- * have any other '\0' bytes).
- *
- * For a client-side #SoupMessage, @request_body's %data is usually
- * filled in right before libsoup writes the request to the network,
- * but you should not count on this; use soup_message_body_flatten()
- * if you want to ensure that %data is filled in. @response_body's
- * %data will be filled in before #SoupMessage::finished is emitted.
+ * will not necessarily be filled in at all times. When the body
+ * fields are filled in, they will be terminated with a '\0' byte
+ * (which is not included in the <literal>length</literal>), so you
+ * can use them as ordinary C strings (assuming that you know that the
+ * body doesn't have any other '\0' bytes).
+ *
+ * For a client-side #SoupMessage, @request_body's
+ * <literal>data</literal> is usually filled in right before libsoup
+ * writes the request to the network, but you should not count on
+ * this; use soup_message_body_flatten() if you want to ensure that
+ * <literal>data</literal> is filled in. If you are not using
+ * #SoupRequest to read the response, then @response_body's
+ * <literal>data</literal> will be filled in before
+ * #SoupMessage::finished is emitted. (If you are using #SoupRequest,
+ * then the message body is not accumulated by default, so
+ * @response_body's <literal>data</literal> will always be %NULL.)
*
* For a server-side #SoupMessage, @request_body's %data will be
* filled in before #SoupMessage::got_body is emitted.
@@ -121,11 +131,14 @@ enum {
PROP_REASON_PHRASE,
PROP_FIRST_PARTY,
PROP_REQUEST_BODY,
+ PROP_REQUEST_BODY_DATA,
PROP_REQUEST_HEADERS,
PROP_RESPONSE_BODY,
+ PROP_RESPONSE_BODY_DATA,
PROP_RESPONSE_HEADERS,
PROP_TLS_CERTIFICATE,
PROP_TLS_ERRORS,
+ PROP_PRIORITY,
LAST_PROP
};
@@ -136,6 +149,7 @@ soup_message_init (SoupMessage *msg)
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1;
+ priv->priority = SOUP_MESSAGE_PRIORITY_NORMAL;
msg->request_body = soup_message_body_new ();
msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
@@ -162,8 +176,6 @@ soup_message_finalize (GObject *object)
g_slist_free (priv->disabled_features);
- g_slist_free_full (priv->decoders, g_object_unref);
-
g_clear_object (&priv->tls_certificate);
soup_message_body_free (msg->request_body);
@@ -229,6 +241,9 @@ soup_message_set_property (GObject *object, guint prop_id,
else if (priv->tls_certificate)
priv->msg_flags |= SOUP_MESSAGE_CERTIFICATE_TRUSTED;
break;
+ case PROP_PRIORITY:
+ priv->priority = g_value_get_enum (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -241,6 +256,7 @@ soup_message_get_property (GObject *object, guint prop_id,
{
SoupMessage *msg = SOUP_MESSAGE (object);
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupBuffer *buf;
switch (prop_id) {
case PROP_METHOD:
@@ -270,12 +286,22 @@ soup_message_get_property (GObject *object, guint prop_id,
case PROP_REQUEST_BODY:
g_value_set_boxed (value, msg->request_body);
break;
+ case PROP_REQUEST_BODY_DATA:
+ buf = soup_message_body_flatten (msg->request_body);
+ g_value_take_boxed (value, soup_buffer_get_as_bytes (buf));
+ soup_buffer_free (buf);
+ break;
case PROP_REQUEST_HEADERS:
g_value_set_boxed (value, msg->request_headers);
break;
case PROP_RESPONSE_BODY:
g_value_set_boxed (value, msg->response_body);
break;
+ case PROP_RESPONSE_BODY_DATA:
+ buf = soup_message_body_flatten (msg->response_body);
+ g_value_take_boxed (value, soup_buffer_get_as_bytes (buf));
+ soup_buffer_free (buf);
+ break;
case PROP_RESPONSE_HEADERS:
g_value_set_boxed (value, msg->response_headers);
break;
@@ -285,6 +311,9 @@ soup_message_get_property (GObject *object, guint prop_id,
case PROP_TLS_ERRORS:
g_value_set_flags (value, priv->tls_errors);
break;
+ case PROP_PRIORITY:
+ g_value_set_enum (value, priv->priority);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -292,12 +321,12 @@ soup_message_get_property (GObject *object, guint prop_id,
}
static void
-soup_message_real_got_body (SoupMessage *req)
+soup_message_real_got_body (SoupMessage *msg)
{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageBody *body;
- body = priv->server_side ? req->request_body : req->response_body;
+ body = priv->server_side ? msg->request_body : msg->response_body;
if (soup_message_body_get_accumulate (body)) {
SoupBuffer *buffer;
@@ -336,7 +365,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -354,7 +383,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -376,7 +405,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -391,7 +420,7 @@ soup_message_class_init (SoupMessageClass *message_class)
* every successful write() call, not only after finishing a
* complete "chunk".
*
- * Since: 2.4.1
+ * Since: 2.24
**/
signals[WROTE_BODY_DATA] =
g_signal_new ("wrote_body_data",
@@ -399,7 +428,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
0, /* FIXME after next ABI break */
NULL, NULL,
- _soup_marshal_NONE__BOXED,
+ NULL,
G_TYPE_NONE, 1,
SOUP_TYPE_BUFFER);
@@ -420,7 +449,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -443,7 +472,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_informational),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -475,7 +504,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_headers),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -498,7 +527,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
NULL, NULL,
- _soup_marshal_NONE__BOXED,
+ NULL,
G_TYPE_NONE, 1,
/* Use %G_SIGNAL_TYPE_STATIC_SCOPE so that
* the %SOUP_MEMORY_TEMPORARY buffers used
@@ -527,7 +556,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_body),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -556,7 +585,7 @@ soup_message_class_init (SoupMessageClass *message_class)
* that sniffing could be done is delivered on the first
* emission of #SoupMessage::got-chunk.
*
- * Since: 2.27.3
+ * Since: 2.28
**/
signals[CONTENT_SNIFFED] =
g_signal_new ("content_sniffed",
@@ -564,7 +593,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
- _soup_marshal_NONE__STRING_BOXED,
+ NULL,
G_TYPE_NONE, 2,
G_TYPE_STRING,
G_TYPE_HASH_TABLE);
@@ -584,7 +613,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, restarted),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -601,7 +630,7 @@ soup_message_class_init (SoupMessageClass *message_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, finished),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -768,6 +797,28 @@ soup_message_class_init (SoupMessageClass *message_class)
SOUP_TYPE_MESSAGE_BODY,
G_PARAM_READABLE));
/**
+ * SOUP_MESSAGE_REQUEST_BODY_DATA:
+ *
+ * Alias for the #SoupMessage:request-body-data property. (The
+ * message's HTTP request body, as a #GBytes.)
+ *
+ * Since: 2.46
+ **/
+ /**
+ * SoupMessage:request-body-data:
+ *
+ * The message's HTTP request body, as a #GBytes.
+ *
+ * Since: 2.46
+ **/
+ g_object_class_install_property (
+ object_class, PROP_REQUEST_BODY_DATA,
+ g_param_spec_boxed (SOUP_MESSAGE_REQUEST_BODY_DATA,
+ "Request Body Data",
+ "The HTTP request body",
+ G_TYPE_BYTES,
+ G_PARAM_READABLE));
+ /**
* SOUP_MESSAGE_REQUEST_HEADERS:
*
* Alias for the #SoupMessage:request-headers property. (The
@@ -794,6 +845,28 @@ soup_message_class_init (SoupMessageClass *message_class)
SOUP_TYPE_MESSAGE_BODY,
G_PARAM_READABLE));
/**
+ * SOUP_MESSAGE_RESPONSE_BODY_DATA:
+ *
+ * Alias for the #SoupMessage:response-body-data property. (The
+ * message's HTTP response body, as a #GBytes.)
+ *
+ * Since: 2.46
+ **/
+ /**
+ * SoupMessage:response-body-data:
+ *
+ * The message's HTTP response body, as a #GBytes.
+ *
+ * Since: 2.46
+ **/
+ g_object_class_install_property (
+ object_class, PROP_RESPONSE_BODY_DATA,
+ g_param_spec_boxed (SOUP_MESSAGE_RESPONSE_BODY_DATA,
+ "Response Body Data",
+ "The HTTP response body",
+ G_TYPE_BYTES,
+ G_PARAM_READABLE));
+ /**
* SOUP_MESSAGE_RESPONSE_HEADERS:
*
* Alias for the #SoupMessage:response-headers property. (The
@@ -850,6 +923,22 @@ soup_message_class_init (SoupMessageClass *message_class)
"The verification errors on the message's TLS certificate",
G_TYPE_TLS_CERTIFICATE_FLAGS, 0,
G_PARAM_READWRITE));
+ /**
+ * SOUP_MESSAGE_PRIORITY:
+ *
+ * Sets the priority of the #SoupMessage. See
+ * soup_message_set_priority() for further details.
+ *
+ * Since: 2.44
+ **/
+ g_object_class_install_property (
+ object_class, PROP_PRIORITY,
+ g_param_spec_enum (SOUP_MESSAGE_PRIORITY,
+ "Priority",
+ "The priority of the message",
+ SOUP_TYPE_MESSAGE_PRIORITY,
+ SOUP_MESSAGE_PRIORITY_NORMAL,
+ G_PARAM_READWRITE));
}
@@ -906,9 +995,10 @@ soup_message_new_from_uri (const char *method, SoupURI *uri)
/**
* soup_message_set_request:
* @msg: the message
- * @content_type: MIME Content-Type of the body
+ * @content_type: (allow-none): MIME Content-Type of the body
* @req_use: a #SoupMemoryUse describing how to handle @req_body
- * @req_body: a data buffer containing the body of the message request.
+ * @req_body: (allow-none) (array length=req_length) (element-type guint8):
+ * a data buffer containing the body of the message request.
* @req_length: the byte length of @req_body.
*
* Convenience function to set the request body of a #SoupMessage. If
@@ -925,6 +1015,8 @@ soup_message_set_request (SoupMessage *msg,
g_return_if_fail (content_type != NULL || req_length == 0);
if (content_type) {
+ g_warn_if_fail (strchr (content_type, '/') != NULL);
+
soup_message_headers_replace (msg->request_headers,
"Content-Type", content_type);
soup_message_body_append (msg->request_body, req_use,
@@ -941,8 +1033,8 @@ soup_message_set_request (SoupMessage *msg,
* @msg: the message
* @content_type: (allow-none): MIME Content-Type of the body
* @resp_use: a #SoupMemoryUse describing how to handle @resp_body
- * @resp_body: (array length=resp_length) (element-type guint8): a data buffer
- * containing the body of the message response.
+ * @resp_body: (allow-none) (array length=resp_length) (element-type guint8):
+ * a data buffer containing the body of the message response.
* @resp_length: the byte length of @resp_body.
*
* Convenience function to set the response body of a #SoupMessage. If
@@ -959,6 +1051,8 @@ soup_message_set_response (SoupMessage *msg,
g_return_if_fail (content_type != NULL || resp_length == 0);
if (content_type) {
+ g_warn_if_fail (strchr (content_type, '/') != NULL);
+
soup_message_headers_replace (msg->response_headers,
"Content-Type", content_type);
soup_message_body_append (msg->response_body, resp_use,
@@ -970,150 +1064,66 @@ soup_message_set_response (SoupMessage *msg,
}
}
-/**
- * soup_message_wrote_informational:
- * @msg: a #SoupMessage
- *
- * Emits the %wrote_informational signal, indicating that the IO layer
- * finished writing an informational (1xx) response for @msg.
- **/
void
soup_message_wrote_informational (SoupMessage *msg)
{
g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
}
-/**
- * soup_message_wrote_headers:
- * @msg: a #SoupMessage
- *
- * Emits the %wrote_headers signal, indicating that the IO layer
- * finished writing the (non-informational) headers for @msg.
- **/
void
soup_message_wrote_headers (SoupMessage *msg)
{
g_signal_emit (msg, signals[WROTE_HEADERS], 0);
}
-/**
- * soup_message_wrote_chunk:
- * @msg: a #SoupMessage
- *
- * Emits the %wrote_chunk signal, indicating that the IO layer
- * finished writing a chunk of @msg's body.
- **/
void
soup_message_wrote_chunk (SoupMessage *msg)
{
g_signal_emit (msg, signals[WROTE_CHUNK], 0);
}
-/**
- * soup_message_wrote_body_data:
- * @msg: a #SoupMessage
- * @chunk: the data written
- *
- * Emits the %wrote_body_data signal, indicating that the IO layer
- * finished writing a portion of @msg's body.
- **/
void
soup_message_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk)
{
g_signal_emit (msg, signals[WROTE_BODY_DATA], 0, chunk);
}
-/**
- * soup_message_wrote_body:
- * @msg: a #SoupMessage
- *
- * Emits the %wrote_body signal, indicating that the IO layer finished
- * writing the body for @msg.
- **/
void
soup_message_wrote_body (SoupMessage *msg)
{
g_signal_emit (msg, signals[WROTE_BODY], 0);
}
-/**
- * soup_message_got_informational:
- * @msg: a #SoupMessage
- *
- * Emits the #SoupMessage::got_informational signal, indicating that
- * the IO layer read a complete informational (1xx) response for @msg.
- **/
void
soup_message_got_informational (SoupMessage *msg)
{
g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
}
-/**
- * soup_message_got_headers:
- * @msg: a #SoupMessage
- *
- * Emits the #SoupMessage::got_headers signal, indicating that the IO
- * layer finished reading the (non-informational) headers for @msg.
- **/
void
soup_message_got_headers (SoupMessage *msg)
{
g_signal_emit (msg, signals[GOT_HEADERS], 0);
}
-/**
- * soup_message_got_chunk:
- * @msg: a #SoupMessage
- * @chunk: the newly-read chunk
- *
- * Emits the #SoupMessage::got_chunk signal, indicating that the IO
- * layer finished reading a chunk of @msg's body.
- **/
void
soup_message_got_chunk (SoupMessage *msg, SoupBuffer *chunk)
{
g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk);
}
-/**
- * soup_message_got_body:
- * @msg: a #SoupMessage
- *
- * Emits the #SoupMessage::got_body signal, indicating that the IO
- * layer finished reading the body for @msg.
- **/
void
soup_message_got_body (SoupMessage *msg)
{
g_signal_emit (msg, signals[GOT_BODY], 0);
}
-/**
- * soup_message_content_sniffed:
- * @msg: a #SoupMessage
- * @content_type: a string with the sniffed content type
- * @params: a #GHashTable with the parameters
- *
- * Emits the %content_sniffed signal, indicating that the IO layer
- * finished sniffing the content type for @msg. If content sniffing
- * will not be performed, due to the sniffer deciding to trust the
- * Content-Type sent by the server, this signal is emitted immediately
- * after #SoupMessage::got_headers, with %NULL as @content_type.
- **/
void
soup_message_content_sniffed (SoupMessage *msg, const char *content_type, GHashTable *params)
{
g_signal_emit (msg, signals[CONTENT_SNIFFED], 0, content_type, params);
}
-/**
- * soup_message_restarted:
- * @msg: a #SoupMessage
- *
- * Emits the %restarted signal, indicating that @msg should be
- * requeued.
- **/
void
soup_message_restarted (SoupMessage *msg)
{
@@ -1125,13 +1135,6 @@ soup_message_restarted (SoupMessage *msg)
g_signal_emit (msg, signals[RESTARTED], 0);
}
-/**
- * soup_message_finished:
- * @msg: a #SoupMessage
- *
- * Emits the %finished signal, indicating that @msg has been completely
- * processed.
- **/
void
soup_message_finished (SoupMessage *msg)
{
@@ -1163,11 +1166,6 @@ header_handler_metamarshal (GClosure *closure, GValue *return_value,
const char *header_name = marshal_data;
SoupMessageHeaders *hdrs;
-#ifdef FIXME
- if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
- return;
-#endif
-
hdrs = priv->server_side ? msg->request_headers : msg->response_headers;
if (soup_message_headers_get_one (hdrs, header_name)) {
closure->marshal (closure, return_value, n_param_values,
@@ -1185,16 +1183,11 @@ header_handler_metamarshal (GClosure *closure, GValue *return_value,
* @user_data: data to pass to @handler_cb
*
* Adds a signal handler to @msg for @signal, as with
- * g_signal_connect(), but with two differences: the @callback will
- * only be run if @msg has a header named @header, and it will only be
- * run if no earlier handler cancelled or requeued the message.
- *
- * If @signal is one of the "got" signals (eg, "got_headers"), or
- * "finished" or "restarted", then @header is matched against the
- * incoming message headers (that is, the #request_headers for a
- * client #SoupMessage, or the #response_headers for a server
- * #SoupMessage). If @signal is one of the "wrote" signals, then
- * @header is matched against the outgoing message headers.
+ * g_signal_connect(), but the @callback will only be run if @msg's
+ * incoming messages headers (that is, the
+ * <literal>request_headers</literal> for a client #SoupMessage, or
+ * the <literal>response_headers</literal> for a server #SoupMessage)
+ * contain a header named @header.
*
* Return value: the handler ID from g_signal_connect()
**/
@@ -1232,11 +1225,6 @@ status_handler_metamarshal (GClosure *closure, GValue *return_value,
SoupMessage *msg = g_value_get_object (&param_values[0]);
guint status = GPOINTER_TO_UINT (marshal_data);
-#ifdef FIXME
- if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
- return;
-#endif
-
if (msg->status_code == status) {
closure->marshal (closure, return_value, n_param_values,
param_values, invocation_hint,
@@ -1253,9 +1241,8 @@ status_handler_metamarshal (GClosure *closure, GValue *return_value,
* @user_data: data to pass to @handler_cb
*
* Adds a signal handler to @msg for @signal, as with
- * g_signal_connect() but with two differences: the @callback will
- * only be run if @msg has the status @status_code, and it will only
- * be run if no earlier handler cancelled or requeued the message.
+ * g_signal_connect(), but the @callback will only be run if @msg has
+ * the status @status_code.
*
* @signal must be a signal that will be emitted after @msg's status
* is set. For a client #SoupMessage, this means it can't be a "wrote"
@@ -1285,15 +1272,6 @@ soup_message_add_status_code_handler (SoupMessage *msg,
}
-/**
- * soup_message_set_auth:
- * @msg: a #SoupMessage
- * @auth: a #SoupAuth, or %NULL
- *
- * Sets @msg to authenticate to its destination using @auth, which
- * must have already been fully authenticated. If @auth is %NULL, @msg
- * will not authenticate to its destination.
- **/
void
soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
{
@@ -1302,7 +1280,6 @@ soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
g_return_if_fail (SOUP_IS_MESSAGE (msg));
g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
- g_return_if_fail (auth == NULL || soup_auth_is_authenticated (auth));
priv = SOUP_MESSAGE_GET_PRIVATE (msg);
@@ -1317,20 +1294,13 @@ soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
g_object_ref (priv->auth);
token = soup_auth_get_authorization (auth, msg);
- soup_message_headers_replace (msg->request_headers,
- "Authorization", token);
- g_free (token);
+ if (token) {
+ soup_message_headers_replace (msg->request_headers,
+ "Authorization", token);
+ g_free (token);
+ }
}
-/**
- * soup_message_get_auth:
- * @msg: a #SoupMessage
- *
- * Gets the #SoupAuth used by @msg for authentication.
- *
- * Return value: (transfer none): the #SoupAuth used by @msg for
- * authentication, or %NULL if @msg is unauthenticated.
- **/
SoupAuth *
soup_message_get_auth (SoupMessage *msg)
{
@@ -1339,15 +1309,6 @@ soup_message_get_auth (SoupMessage *msg)
return SOUP_MESSAGE_GET_PRIVATE (msg)->auth;
}
-/**
- * soup_message_set_proxy_auth:
- * @msg: a #SoupMessage
- * @auth: a #SoupAuth, or %NULL
- *
- * Sets @msg to authenticate to its proxy using @auth, which must have
- * already been fully authenticated. If @auth is %NULL, @msg will not
- * authenticate to its proxy.
- **/
void
soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
{
@@ -1356,7 +1317,6 @@ soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
g_return_if_fail (SOUP_IS_MESSAGE (msg));
g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
- g_return_if_fail (auth == NULL || soup_auth_is_authenticated (auth));
priv = SOUP_MESSAGE_GET_PRIVATE (msg);
@@ -1376,15 +1336,6 @@ soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
g_free (token);
}
-/**
- * soup_message_get_proxy_auth:
- * @msg: a #SoupMessage
- *
- * Gets the #SoupAuth used by @msg for authentication to its proxy..
- *
- * Return value: the #SoupAuth used by @msg for authentication to its
- * proxy, or %NULL if @msg isn't authenticated to its proxy.
- **/
SoupAuth *
soup_message_get_proxy_auth (SoupMessage *msg)
{
@@ -1393,43 +1344,54 @@ soup_message_get_proxy_auth (SoupMessage *msg)
return SOUP_MESSAGE_GET_PRIVATE (msg)->proxy_auth;
}
+SoupConnection *
+soup_message_get_connection (SoupMessage *msg)
+{
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->connection;
+}
+
+void
+soup_message_set_connection (SoupMessage *msg,
+ SoupConnection *conn)
+{
+ SOUP_MESSAGE_GET_PRIVATE (msg)->connection = conn;
+}
+
/**
* soup_message_cleanup_response:
- * @req: a #SoupMessage
+ * @msg: a #SoupMessage
*
- * Cleans up all response data on @req, so that the request can be sent
+ * Cleans up all response data on @msg, so that the request can be sent
* again and receive a new response. (Eg, as a result of a redirect or
* authorization request.)
**/
void
-soup_message_cleanup_response (SoupMessage *req)
+soup_message_cleanup_response (SoupMessage *msg)
{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
- soup_message_body_truncate (req->response_body);
- soup_message_headers_clear (req->response_headers);
+ soup_message_body_truncate (msg->response_body);
+ soup_message_headers_clear (msg->response_headers);
if (priv->server_side) {
- soup_message_headers_set_encoding (req->response_headers,
+ soup_message_headers_set_encoding (msg->response_headers,
SOUP_ENCODING_CONTENT_LENGTH);
}
- g_slist_free_full (priv->decoders, g_object_unref);
- priv->decoders = NULL;
priv->msg_flags &= ~SOUP_MESSAGE_CONTENT_DECODED;
- req->status_code = SOUP_STATUS_NONE;
- if (req->reason_phrase) {
- g_free (req->reason_phrase);
- req->reason_phrase = NULL;
+ msg->status_code = SOUP_STATUS_NONE;
+ if (msg->reason_phrase) {
+ g_free (msg->reason_phrase);
+ msg->reason_phrase = NULL;
}
priv->http_version = priv->orig_http_version;
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_FLAGS);
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_TLS_CERTIFICATE);
- g_object_notify (G_OBJECT (req), SOUP_MESSAGE_TLS_ERRORS);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_HTTP_VERSION);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FLAGS);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_TLS_CERTIFICATE);
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_TLS_ERRORS);
}
/**
@@ -1450,10 +1412,15 @@ soup_message_cleanup_response (SoupMessage *req)
* @SOUP_MESSAGE_CERTIFICATE_TRUSTED: if set after an https response
* has been received, indicates that the server's SSL certificate is
* trusted according to the session's CA.
- * @SOUP_MESSAGE_NEW_CONNECTION: The message should be sent on a
- * newly-created connection, not reusing an existing persistent
- * connection. Note that messages with non-idempotent
- * #SoupMessage:method<!-- -->s behave this way by default.
+ * @SOUP_MESSAGE_NEW_CONNECTION: Requests that the message should be
+ * sent on a newly-created connection, not reusing an existing
+ * persistent connection. Note that messages with non-idempotent
+ * #SoupMessage:method<!-- -->s behave this way by default, unless
+ * #SOUP_MESSAGE_IDEMPOTENT is set.
+ * @SOUP_MESSAGE_IDEMPOTENT: The message is considered idempotent,
+ * regardless its #SoupMessage:method, and allows reuse of existing
+ * idle connections, instead of always requiring a new one, unless
+ * #SOUP_MESSAGE_NEW_CONNECTION is set.
*
* Various flags that can be set on a #SoupMessage to alter its
* behavior.
@@ -1739,6 +1706,9 @@ soup_message_set_status_full (SoupMessage *msg,
* becomes possible to allocate a new buffer.
*
* Return value: the new buffer (or %NULL)
+ *
+ * Deprecated: Use #SoupRequest if you want to read into your
+ * own buffers.
**/
/**
@@ -1750,11 +1720,13 @@ soup_message_set_status_full (SoupMessage *msg,
* destroyed
*
* Sets an alternate chunk-allocation function to use when reading
- * @msg's body. Every time data is available to read, libsoup will
- * call @allocator, which should return a #SoupBuffer. (See
- * #SoupChunkAllocator for additional details.) Libsoup will then read
- * data from the network into that buffer, and update the buffer's
- * <literal>length</literal> to indicate how much data it read.
+ * @msg's body when using the traditional (ie,
+ * non-#SoupRequest<!-- -->-based) API. Every time data is available
+ * to read, libsoup will call @allocator, which should return a
+ * #SoupBuffer. (See #SoupChunkAllocator for additional details.)
+ * Libsoup will then read data from the network into that buffer, and
+ * update the buffer's <literal>length</literal> to indicate how much
+ * data it read.
*
* Generally, a custom chunk allocator would be used in conjunction
* with soup_message_body_set_accumulate() %FALSE and
@@ -1775,6 +1747,10 @@ soup_message_set_status_full (SoupMessage *msg,
* you'll need to ref the #SoupBuffer (or its owner, in the
* soup_buffer_new_with_owner() case) to ensure that the data remains
* valid.
+ *
+ * Deprecated: #SoupRequest provides a much simpler API that lets you
+ * read the response directly into your own buffers without needing to
+ * mess with callbacks, pausing/unpausing, etc.
**/
void
soup_message_set_chunk_allocator (SoupMessage *msg,
@@ -1804,10 +1780,8 @@ soup_message_set_chunk_allocator (SoupMessage *msg,
* This disables the actions of #SoupSessionFeature<!-- -->s with the
* given @feature_type (or a subclass of that type) on @msg, so that
* @msg is processed as though the feature(s) hadn't been added to the
- * session. Eg, passing #SOUP_TYPE_PROXY_URI_RESOLVER for @feature_type
- * will disable proxy handling and cause @msg to be sent directly to
- * the indicated origin server, regardless of system proxy
- * configuration.
+ * session. Eg, passing #SOUP_TYPE_CONTENT_SNIFFER for @feature_type
+ * will disable Content-Type sniffing on the message.
*
* You must call this before queueing @msg on a session; calling it on
* a message that has already been queued is undefined. In particular,
@@ -1934,11 +1908,13 @@ soup_message_set_https_status (SoupMessage *msg, SoupConnection *conn)
* @certificate: (out) (transfer none): @msg's TLS certificate
* @errors: (out): the verification status of @certificate
*
- * If @msg is using https, this retrieves the #GTlsCertificate
- * associated with its connection, and the #GTlsCertificateFlags showing
- * what problems, if any, have been found with that certificate.
+ * If @msg is using https (or attempted to use https but got
+ * %SOUP_STATUS_SSL_FAILED), this retrieves the #GTlsCertificate
+ * associated with its connection, and the #GTlsCertificateFlags
+ * showing what problems, if any, have been found with that
+ * certificate.
*
- * Return value: %TRUE if @msg uses https, %FALSE if not
+ * Return value: %TRUE if @msg used/attempted https, %FALSE if not
*
* Since: 2.34
*/
@@ -1994,3 +1970,102 @@ soup_message_set_redirect (SoupMessage *msg, guint status_code,
g_free (location_str);
soup_uri_free (location);
}
+
+void
+soup_message_set_soup_request (SoupMessage *msg,
+ SoupRequest *req)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ priv->request = req;
+}
+
+/**
+ * soup_message_get_soup_request:
+ * @msg: a #SoupMessage
+ *
+ * If @msg is associated with a #SoupRequest, this returns that
+ * request. Otherwise it returns %NULL.
+ *
+ * Return value: (transfer none): @msg's associated #SoupRequest
+ *
+ * Since: 2.42
+ */
+SoupRequest *
+soup_message_get_soup_request (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ return priv->request;
+}
+
+/**
+ * SoupMessagePriority:
+ * @SOUP_MESSAGE_PRIORITY_VERY_LOW: The lowest priority, the messages
+ * with this priority will be the last ones to be attended.
+ * @SOUP_MESSAGE_PRIORITY_LOW: Use this for low priority messages, a
+ * #SoupMessage with the default priority will be processed first.
+ * @SOUP_MESSAGE_PRIORITY_NORMAL: The default priotity, this is the
+ * priority assigned to the #SoupMessage by default.
+ * @SOUP_MESSAGE_PRIORITY_HIGH: High priority, a #SoupMessage with
+ * this priority will be processed before the ones with the default
+ * priority.
+ * @SOUP_MESSAGE_PRIORITY_VERY_HIGH: The highest priority, use this
+ * for very urgent #SoupMessage as they will be the first ones to be
+ * attended.
+ *
+ * Priorities that can be set on a #SoupMessage to instruct the
+ * message queue to process it before any other message with lower
+ * priority.
+ **/
+
+/**
+ * soup_message_set_priority:
+ * @msg: a #SoupMessage
+ * @priority: the #SoupMessagePriority
+ *
+ * Sets the priority of a message. Note that this won't have any
+ * effect unless used before the message is added to the session's
+ * message processing queue.
+ *
+ * The message will be placed just before any other previously added
+ * message with lower priority (messages with the same priority are
+ * processed on a FIFO basis).
+ *
+ * Setting priorities does not currently work with #SoupSessionSync
+ * (or with synchronous messages on a plain #SoupSession) because in
+ * the synchronous/blocking case, priority ends up being determined
+ * semi-randomly by thread scheduling.
+ *
+ * Since: 2.44
+ */
+void
+soup_message_set_priority (SoupMessage *msg,
+ SoupMessagePriority priority)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ g_object_set (msg, SOUP_MESSAGE_PRIORITY, priority, NULL);
+}
+
+/**
+ * soup_message_get_priority:
+ * @msg: a #SoupMessage
+ *
+ * Retrieves the #SoupMessagePriority. If not set this value defaults
+ * to #SOUP_MESSAGE_PRIORITY_NORMAL.
+ *
+ * Return value: the priority of the message.
+ *
+ * Since: 2.44
+ */
+SoupMessagePriority
+soup_message_get_priority (SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_MESSAGE_PRIORITY_NORMAL);
+
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->priority;
+}
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index d3c7e3c5..b02d293b 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -60,20 +60,23 @@ typedef struct {
GType soup_message_get_type (void);
-#define SOUP_MESSAGE_METHOD "method"
-#define SOUP_MESSAGE_URI "uri"
-#define SOUP_MESSAGE_HTTP_VERSION "http-version"
-#define SOUP_MESSAGE_FLAGS "flags"
-#define SOUP_MESSAGE_SERVER_SIDE "server-side"
-#define SOUP_MESSAGE_STATUS_CODE "status-code"
-#define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
-#define SOUP_MESSAGE_FIRST_PARTY "first-party"
-#define SOUP_MESSAGE_REQUEST_BODY "request-body"
-#define SOUP_MESSAGE_REQUEST_HEADERS "request-headers"
-#define SOUP_MESSAGE_RESPONSE_BODY "response-body"
-#define SOUP_MESSAGE_RESPONSE_HEADERS "response-headers"
-#define SOUP_MESSAGE_TLS_CERTIFICATE "tls-certificate"
-#define SOUP_MESSAGE_TLS_ERRORS "tls-errors"
+#define SOUP_MESSAGE_METHOD "method"
+#define SOUP_MESSAGE_URI "uri"
+#define SOUP_MESSAGE_HTTP_VERSION "http-version"
+#define SOUP_MESSAGE_FLAGS "flags"
+#define SOUP_MESSAGE_SERVER_SIDE "server-side"
+#define SOUP_MESSAGE_STATUS_CODE "status-code"
+#define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
+#define SOUP_MESSAGE_FIRST_PARTY "first-party"
+#define SOUP_MESSAGE_REQUEST_BODY "request-body"
+#define SOUP_MESSAGE_REQUEST_BODY_DATA "request-body-data"
+#define SOUP_MESSAGE_REQUEST_HEADERS "request-headers"
+#define SOUP_MESSAGE_RESPONSE_BODY "response-body"
+#define SOUP_MESSAGE_RESPONSE_BODY_DATA "response-body-data"
+#define SOUP_MESSAGE_RESPONSE_HEADERS "response-headers"
+#define SOUP_MESSAGE_TLS_CERTIFICATE "tls-certificate"
+#define SOUP_MESSAGE_TLS_ERRORS "tls-errors"
+#define SOUP_MESSAGE_PRIORITY "priority"
SoupMessage *soup_message_new (const char *method,
const char *uri_string);
@@ -105,21 +108,25 @@ gboolean soup_message_is_keepalive (SoupMessage *msg);
SoupURI *soup_message_get_uri (SoupMessage *msg);
void soup_message_set_uri (SoupMessage *msg,
SoupURI *uri);
+SOUP_AVAILABLE_IN_2_26
SoupAddress *soup_message_get_address (SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_30
SoupURI *soup_message_get_first_party (SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_30
void soup_message_set_first_party (SoupMessage *msg,
SoupURI *first_party);
typedef enum {
SOUP_MESSAGE_NO_REDIRECT = (1 << 1),
SOUP_MESSAGE_CAN_REBUILD = (1 << 2),
-#ifndef LIBSOUP_DISABLE_DEPRECATED
+#ifndef SOUP_DISABLE_DEPRECATED
SOUP_MESSAGE_OVERWRITE_CHUNKS = (1 << 3),
#endif
SOUP_MESSAGE_CONTENT_DECODED = (1 << 4),
SOUP_MESSAGE_CERTIFICATE_TRUSTED = (1 << 5),
- SOUP_MESSAGE_NEW_CONNECTION = (1 << 6)
+ SOUP_MESSAGE_NEW_CONNECTION = (1 << 6),
+ SOUP_MESSAGE_IDEMPOTENT = (1 << 7)
} SoupMessageFlags;
void soup_message_set_flags (SoupMessage *msg,
@@ -127,6 +134,7 @@ void soup_message_set_flags (SoupMessage *msg,
SoupMessageFlags soup_message_get_flags (SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_34
gboolean soup_message_get_https_status (SoupMessage *msg,
GTlsCertificate **certificate,
GTlsCertificateFlags *errors);
@@ -156,23 +164,48 @@ void soup_message_set_status_full (SoupMessage *msg,
guint status_code,
const char *reason_phrase);
+SOUP_AVAILABLE_IN_2_38
void soup_message_set_redirect (SoupMessage *msg,
guint status_code,
const char *redirect_uri);
/* I/O */
+#ifndef SOUP_DISABLE_DEPRECATED
typedef SoupBuffer * (*SoupChunkAllocator) (SoupMessage *msg,
gsize max_len,
gpointer user_data);
+SOUP_DEPRECATED_IN_2_42_FOR(SoupRequest)
void soup_message_set_chunk_allocator (SoupMessage *msg,
SoupChunkAllocator allocator,
gpointer user_data,
GDestroyNotify destroy_notify);
+#endif
+SOUP_AVAILABLE_IN_2_28
void soup_message_disable_feature (SoupMessage *msg,
GType feature_type);
+SOUP_AVAILABLE_IN_2_42
+SoupRequest *soup_message_get_soup_request (SoupMessage *msg);
+
+
+typedef enum {
+ SOUP_MESSAGE_PRIORITY_VERY_LOW = 0,
+ SOUP_MESSAGE_PRIORITY_LOW,
+ SOUP_MESSAGE_PRIORITY_NORMAL,
+ SOUP_MESSAGE_PRIORITY_HIGH,
+ SOUP_MESSAGE_PRIORITY_VERY_HIGH
+} SoupMessagePriority;
+
+SOUP_AVAILABLE_IN_2_44
+void soup_message_set_priority (SoupMessage *msg,
+ SoupMessagePriority priority);
+
+
+SOUP_AVAILABLE_IN_2_44
+SoupMessagePriority soup_message_get_priority (SoupMessage *msg);
+
void soup_message_wrote_informational (SoupMessage *msg);
void soup_message_wrote_headers (SoupMessage *msg);
void soup_message_wrote_chunk (SoupMessage *msg);
diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h
index 8276b8a5..50a3b988 100644
--- a/libsoup/soup-misc-private.h
+++ b/libsoup/soup-misc-private.h
@@ -4,26 +4,48 @@
* Copyright 2011 Red Hat, Inc.
*/
-#ifndef SOUP_URI_PRIVATE_H
-#define SOUP_URI_PRIVATE_H 1
+#ifndef SOUP_MISC_PRIVATE_H
+#define SOUP_MISC_PRIVATE_H 1
#include "soup-socket.h"
+#include "soup-message-headers.h"
-char *uri_decoded_copy (const char *str, int length);
+char *soup_uri_decoded_copy (const char *str, int length, int *decoded_length);
+char *soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query,
+ gboolean force_port);
+gboolean soup_uri_is_http (SoupURI *uri, char **aliases);
+gboolean soup_uri_is_https (SoupURI *uri, char **aliases);
-guint soup_socket_handshake_sync (SoupSocket *sock,
- GCancellable *cancellable);
-void soup_socket_handshake_async (SoupSocket *sock,
- GCancellable *cancellable,
- SoupSocketCallback callback,
- gpointer user_data);
+gboolean soup_socket_connect_sync_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GError **error);
+void soup_socket_connect_async_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_socket_connect_finish_internal (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error);
+
+gboolean soup_socket_handshake_sync (SoupSocket *sock,
+ const char *host,
+ GCancellable *cancellable,
+ GError **error);
+void soup_socket_handshake_async (SoupSocket *sock,
+ const char *host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_socket_handshake_finish (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error);
GSocket *soup_socket_get_gsocket (SoupSocket *sock);
GIOStream *soup_socket_get_connection (SoupSocket *sock);
GIOStream *soup_socket_get_iostream (SoupSocket *sock);
#define SOUP_SOCKET_CLEAN_DISPOSE "clean-dispose"
-#define SOUP_SOCKET_USE_PROXY "use-proxy"
+#define SOUP_SOCKET_PROXY_RESOLVER "proxy-resolver"
SoupURI *soup_socket_get_http_proxy_uri (SoupSocket *sock);
/* At some point it might be possible to mark additional methods
@@ -41,4 +63,14 @@ SoupURI *soup_socket_get_http_proxy_uri (SoupSocket *sock);
method == SOUP_METHOD_PUT || \
method == SOUP_METHOD_DELETE)
-#endif /* SOUP_URI_PRIVATE_H */
+GSource *soup_add_completion_reffed (GMainContext *async_context,
+ GSourceFunc function,
+ gpointer data);
+
+guint soup_message_headers_get_ranges_internal (SoupMessageHeaders *hdrs,
+ goffset total_length,
+ gboolean check_satisfiable,
+ SoupRange **ranges,
+ int *length);
+
+#endif /* SOUP_MISC_PRIVATE_H */
diff --git a/libsoup/soup-misc.c b/libsoup/soup-misc.c
index 83540457..325b5872 100644
--- a/libsoup/soup-misc.c
+++ b/libsoup/soup-misc.c
@@ -8,6 +8,7 @@
#include <string.h>
#include "soup-misc.h"
+#include "soup-misc-private.h"
/**
* SECTION:soup-misc
@@ -112,6 +113,19 @@ soup_add_idle (GMainContext *async_context,
return source;
}
+GSource *
+soup_add_completion_reffed (GMainContext *async_context,
+ GSourceFunc function,
+ gpointer data)
+{
+ GSource *source = g_idle_source_new ();
+
+ g_source_set_priority (source, G_PRIORITY_DEFAULT);
+ g_source_set_callback (source, function, data, NULL);
+ g_source_attach (source, async_context);
+ return source;
+}
+
/**
* soup_add_completion: (skip)
* @async_context: (allow-none): the #GMainContext to dispatch the I/O
@@ -132,10 +146,9 @@ GSource *
soup_add_completion (GMainContext *async_context,
GSourceFunc function, gpointer data)
{
- GSource *source = g_idle_source_new ();
- g_source_set_priority (source, G_PRIORITY_DEFAULT);
- g_source_set_callback (source, function, data, NULL);
- g_source_attach (source, async_context);
+ GSource *source;
+
+ source = soup_add_completion_reffed (async_context, function, data);
g_source_unref (source);
return source;
}
diff --git a/libsoup/soup-misc.h b/libsoup/soup-misc.h
index 0807b5f7..534b59d6 100644
--- a/libsoup/soup-misc.h
+++ b/libsoup/soup-misc.h
@@ -6,7 +6,7 @@
#ifndef SOUP_MISC_H
#define SOUP_MISC_H 1
-#include <glib-object.h>
+#include <libsoup/soup-types.h>
G_BEGIN_DECLS
@@ -19,6 +19,7 @@ GSource *soup_add_io_watch (GMainContext *async_context,
GSource *soup_add_idle (GMainContext *async_context,
GSourceFunc function,
gpointer data);
+SOUP_AVAILABLE_IN_2_24
GSource *soup_add_completion (GMainContext *async_context,
GSourceFunc function,
gpointer data);
diff --git a/libsoup/soup-multipart-input-stream.c b/libsoup/soup-multipart-input-stream.c
new file mode 100644
index 00000000..dee4e59f
--- /dev/null
+++ b/libsoup/soup-multipart-input-stream.c
@@ -0,0 +1,592 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-multipart-input-stream.c
+ *
+ * Copyright (C) 2012 Collabora Ltd.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-body-input-stream.h"
+#include "soup-filter-input-stream.h"
+#include "soup-enum-types.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-multipart-input-stream.h"
+
+#define RESPONSE_BLOCK_SIZE 8192
+
+/**
+ * SECTION:soup-multipart-input-stream
+ * @short_description: Multipart input handling stream
+ *
+ * This adds support for the multipart responses. For handling the
+ * multiple parts the user needs to wrap the #GInputStream obtained by
+ * sending the request with a #SoupMultipartInputStream and use
+ * soup_multipart_input_stream_next_part() before reading. Responses
+ * which are not wrapped will be treated like non-multipart responses.
+ *
+ * Note that although #SoupMultipartInputStream is a #GInputStream,
+ * you should not read directly from it, and the results are undefined
+ * if you do.
+ *
+ * Since: 2.40
+ **/
+
+static void soup_multipart_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (SoupMultipartInputStream, soup_multipart_input_stream, G_TYPE_FILTER_INPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
+ soup_multipart_input_stream_pollable_init))
+
+enum {
+ PROP_0,
+
+ PROP_MESSAGE,
+};
+
+struct _SoupMultipartInputStreamPrivate {
+ SoupMessage *msg;
+
+ gboolean done_with_part;
+
+ GByteArray *meta_buf;
+ SoupMessageHeaders *current_headers;
+
+ SoupFilterInputStream *base_stream;
+
+ char *boundary;
+ gsize boundary_size;
+
+ goffset remaining_bytes;
+};
+
+static void
+soup_multipart_input_stream_dispose (GObject *object)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (object);
+
+ g_clear_object (&multipart->priv->msg);
+ g_clear_object (&multipart->priv->base_stream);
+
+ G_OBJECT_CLASS (soup_multipart_input_stream_parent_class)->dispose (object);
+}
+
+static void
+soup_multipart_input_stream_finalize (GObject *object)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (object);
+
+ g_free (multipart->priv->boundary);
+
+ if (multipart->priv->meta_buf)
+ g_clear_pointer (&multipart->priv->meta_buf, g_byte_array_unref);
+
+ G_OBJECT_CLASS (soup_multipart_input_stream_parent_class)->finalize (object);
+}
+
+static void
+soup_multipart_input_stream_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_MESSAGE:
+ multipart->priv->msg = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_multipart_input_stream_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (object);
+
+ switch (prop_id) {
+ case PROP_MESSAGE:
+ g_value_set_object (value, multipart->priv->msg);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gssize
+soup_multipart_input_stream_read_real (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (stream);
+ SoupMultipartInputStreamPrivate *priv = multipart->priv;
+ gboolean got_boundary = FALSE;
+ gssize nread = 0;
+ guint8 *buf;
+
+ g_return_val_if_fail (priv->boundary != NULL, -1);
+
+ /* If we have received a Content-Length, and are not yet close to the end of
+ * the part, let's not look for the boundary for now. This optimization is
+ * necessary for keeping CPU usage civil.
+ */
+ if (priv->remaining_bytes > priv->boundary_size) {
+ goffset bytes_to_read = MIN (count, priv->remaining_bytes - priv->boundary_size);
+
+ nread = g_pollable_stream_read (G_INPUT_STREAM (priv->base_stream),
+ buffer, bytes_to_read, blocking,
+ cancellable, error);
+
+ if (nread > 0)
+ priv->remaining_bytes -= nread;
+
+ return nread;
+ }
+
+ if (priv->done_with_part)
+ return 0;
+
+ nread = soup_filter_input_stream_read_until (priv->base_stream, buffer, count,
+ priv->boundary, priv->boundary_size,
+ blocking, FALSE, &got_boundary,
+ cancellable, error);
+
+ if (nread <= 0)
+ return nread;
+
+ if (!got_boundary)
+ return nread;
+
+ priv->done_with_part = TRUE;
+
+ /* Ignore the newline that preceded the boundary. */
+ if (nread == 1) {
+ buf = ((guint8*)buffer);
+ if (!memcmp (buf, "\n", 1))
+ nread -= 1;
+ } else {
+ buf = ((guint8*)buffer) + nread - 2;
+ if (!memcmp (buf, "\r\n", 2))
+ nread -= 2;
+ else if (!memcmp (buf, "\n", 1))
+ nread -= 1;
+ }
+
+ return nread;
+}
+
+static gssize
+soup_multipart_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return soup_multipart_input_stream_read_real (stream, buffer, count,
+ TRUE, cancellable, error);
+}
+
+static void
+soup_multipart_input_stream_init (SoupMultipartInputStream *multipart)
+{
+ SoupMultipartInputStreamPrivate *priv;
+ priv = multipart->priv = G_TYPE_INSTANCE_GET_PRIVATE (multipart,
+ SOUP_TYPE_MULTIPART_INPUT_STREAM,
+ SoupMultipartInputStreamPrivate);
+
+ priv->meta_buf = g_byte_array_sized_new (RESPONSE_BLOCK_SIZE);
+ priv->done_with_part = FALSE;
+}
+
+static void
+soup_multipart_input_stream_constructed (GObject *object)
+{
+ SoupMultipartInputStream *multipart;
+ SoupMultipartInputStreamPrivate *priv;
+ GInputStream *base_stream;
+ const char* boundary;
+ GHashTable *params = NULL;
+
+ multipart = SOUP_MULTIPART_INPUT_STREAM (object);
+ priv = multipart->priv;
+
+ base_stream = G_FILTER_INPUT_STREAM (multipart)->base_stream;
+ priv->base_stream = SOUP_FILTER_INPUT_STREAM (soup_filter_input_stream_new (base_stream));
+
+ soup_message_headers_get_content_type (priv->msg->response_headers,
+ &params);
+
+ boundary = g_hash_table_lookup (params, "boundary");
+ if (boundary) {
+ if (g_str_has_prefix (boundary, "--"))
+ priv->boundary = g_strdup (boundary);
+ else
+ priv->boundary = g_strdup_printf ("--%s", boundary);
+
+ priv->boundary_size = strlen (priv->boundary);
+ } else {
+ g_warning ("No boundary found in message tagged as multipart.");
+ }
+
+ g_hash_table_destroy (params);
+
+ if (G_OBJECT_CLASS (soup_multipart_input_stream_parent_class)->constructed)
+ G_OBJECT_CLASS (soup_multipart_input_stream_parent_class)->constructed (object);
+}
+
+static gboolean
+soup_multipart_input_stream_is_readable (GPollableInputStream *stream)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (stream);
+ SoupMultipartInputStreamPrivate *priv = multipart->priv;
+
+ return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (priv->base_stream));
+}
+
+static gssize
+soup_multipart_input_stream_read_nonblocking (GPollableInputStream *stream,
+ void *buffer,
+ gsize count,
+ GError **error)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (stream);
+
+ return soup_multipart_input_stream_read_real (G_INPUT_STREAM (multipart),
+ buffer, count,
+ FALSE, NULL, error);
+}
+
+static GSource *
+soup_multipart_input_stream_create_source (GPollableInputStream *stream,
+ GCancellable *cancellable)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (stream);
+ SoupMultipartInputStreamPrivate *priv = multipart->priv;
+ GSource *base_source, *pollable_source;
+
+ base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (priv->base_stream), cancellable);
+
+ pollable_source = g_pollable_source_new_full (stream, base_source, cancellable);
+ g_source_unref (base_source);
+
+ return pollable_source;
+}
+
+static void
+soup_multipart_input_stream_class_init (SoupMultipartInputStreamClass *multipart_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (multipart_class);
+ GInputStreamClass *input_stream_class =
+ G_INPUT_STREAM_CLASS (multipart_class);
+
+ g_type_class_add_private (multipart_class, sizeof (SoupMultipartInputStreamPrivate));
+
+ object_class->dispose = soup_multipart_input_stream_dispose;
+ object_class->finalize = soup_multipart_input_stream_finalize;
+ object_class->constructed = soup_multipart_input_stream_constructed;
+ object_class->set_property = soup_multipart_input_stream_set_property;
+ object_class->get_property = soup_multipart_input_stream_get_property;
+
+ input_stream_class->read_fn = soup_multipart_input_stream_read;
+
+ g_object_class_install_property (
+ object_class, PROP_MESSAGE,
+ g_param_spec_object ("message",
+ "Message",
+ "The SoupMessage",
+ SOUP_TYPE_MESSAGE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+}
+
+static void
+soup_multipart_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
+ gpointer interface_data)
+{
+ pollable_interface->is_readable = soup_multipart_input_stream_is_readable;
+ pollable_interface->read_nonblocking = soup_multipart_input_stream_read_nonblocking;
+ pollable_interface->create_source = soup_multipart_input_stream_create_source;
+}
+
+static void
+soup_multipart_input_stream_parse_headers (SoupMultipartInputStream *multipart)
+{
+ SoupMultipartInputStreamPrivate *priv = multipart->priv;
+ gboolean success;
+
+ priv->current_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+
+ /* The part lacks headers, but is there. */
+ if (!priv->meta_buf->len)
+ return;
+
+ success = soup_headers_parse ((const char*) priv->meta_buf->data,
+ (int) priv->meta_buf->len,
+ priv->current_headers);
+
+ if (success)
+ priv->remaining_bytes = soup_message_headers_get_content_length (priv->current_headers);
+ else
+ g_clear_pointer (&priv->current_headers, soup_message_headers_free);
+
+ g_byte_array_remove_range (priv->meta_buf, 0, priv->meta_buf->len);
+}
+
+static gboolean
+soup_multipart_input_stream_read_headers (SoupMultipartInputStream *multipart,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupMultipartInputStreamPrivate *priv = multipart->priv;
+ guchar read_buf[RESPONSE_BLOCK_SIZE];
+ guchar *buf;
+ gboolean got_boundary = FALSE;
+ gboolean got_lf = FALSE;
+ gssize nread = 0;
+
+ g_return_val_if_fail (priv->boundary != NULL, TRUE);
+
+ g_clear_pointer (&priv->current_headers, soup_message_headers_free);
+
+ while (1) {
+ nread = soup_filter_input_stream_read_line (priv->base_stream, read_buf, sizeof (read_buf),
+ /* blocking */ TRUE, &got_lf, cancellable, error);
+
+ if (nread <= 0)
+ break;
+
+ g_byte_array_append (priv->meta_buf, read_buf, nread);
+
+ /* Need to do this boundary check before checking for the line feed, since we
+ * may get the multipart end indicator without getting a new line.
+ */
+ if (!got_boundary &&
+ !strncmp ((char *)priv->meta_buf->data,
+ priv->boundary,
+ priv->boundary_size)) {
+ got_boundary = TRUE;
+
+ /* Now check for possible multipart termination. */
+ buf = &read_buf[nread - 4];
+ if ((nread >= 4 && !memcmp (buf, "--\r\n", 4)) ||
+ (nread >= 3 && !memcmp (buf + 1, "--\n", 3)) ||
+ (nread >= 3 && !memcmp (buf + 2, "--", 2))) {
+ g_byte_array_set_size (priv->meta_buf, 0);
+ return FALSE;
+ }
+ }
+
+ g_return_val_if_fail (got_lf, FALSE);
+
+ /* Discard pre-boundary lines. */
+ if (!got_boundary) {
+ g_byte_array_set_size (priv->meta_buf, 0);
+ continue;
+ }
+
+ if (nread == 1 &&
+ priv->meta_buf->len >= 2 &&
+ !strncmp ((char *)priv->meta_buf->data +
+ priv->meta_buf->len - 2,
+ "\n\n", 2))
+ break;
+ else if (nread == 2 &&
+ priv->meta_buf->len >= 3 &&
+ !strncmp ((char *)priv->meta_buf->data +
+ priv->meta_buf->len - 3,
+ "\n\r\n", 3))
+ break;
+ }
+
+ return TRUE;
+}
+
+/* Public APIs */
+
+/**
+ * soup_multipart_input_stream_new:
+ * @msg: the #SoupMessage the response is related to.
+ * @base_stream: the #GInputStream returned by sending the request.
+ *
+ * Creates a new #SoupMultipartInputStream that wraps the
+ * #GInputStream obtained by sending the #SoupRequest. Reads should
+ * not be done directly through this object, use the input streams
+ * returned by soup_multipart_input_stream_next_part() or its async
+ * counterpart instead.
+ *
+ * Returns: a new #SoupMultipartInputStream
+ *
+ * Since: 2.40
+ **/
+SoupMultipartInputStream *
+soup_multipart_input_stream_new (SoupMessage *msg,
+ GInputStream *base_stream)
+{
+ return g_object_new (SOUP_TYPE_MULTIPART_INPUT_STREAM,
+ "message", msg,
+ "base-stream", base_stream,
+ NULL);
+}
+
+/**
+ * soup_multipart_input_stream_next_part:
+ * @multipart: the #SoupMultipartInputStream
+ * @cancellable: a #GCancellable
+ * @error: a #GError
+ *
+ * Obtains an input stream for the next part. When dealing with a
+ * multipart response the input stream needs to be wrapped in a
+ * #SoupMultipartInputStream and this function or its async
+ * counterpart need to be called to obtain the first part for
+ * reading.
+ *
+ * After calling this function,
+ * soup_multipart_input_stream_get_headers() can be used to obtain the
+ * headers for the first part. A read of 0 bytes indicates the end of
+ * the part; a new call to this function should be done at that point,
+ * to obtain the next part.
+ *
+ * Return value: (transfer full): a new #GInputStream, or %NULL if
+ * there are no more parts
+ *
+ * Since: 2.40
+ */
+GInputStream *
+soup_multipart_input_stream_next_part (SoupMultipartInputStream *multipart,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (!soup_multipart_input_stream_read_headers (multipart, cancellable, error))
+ return NULL;
+
+ soup_multipart_input_stream_parse_headers (multipart);
+
+ multipart->priv->done_with_part = FALSE;
+
+ return G_INPUT_STREAM (g_object_new (SOUP_TYPE_BODY_INPUT_STREAM,
+ "base-stream", G_INPUT_STREAM (multipart),
+ "close-base-stream", FALSE,
+ "encoding", SOUP_ENCODING_EOF,
+ NULL));
+
+}
+
+static void
+soup_multipart_input_stream_next_part_thread (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ SoupMultipartInputStream *multipart = SOUP_MULTIPART_INPUT_STREAM (object);
+ GError *error = NULL;
+ GInputStream *new_stream;
+
+ new_stream = soup_multipart_input_stream_next_part (multipart, cancellable, &error);
+
+ g_input_stream_clear_pending (G_INPUT_STREAM (multipart));
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, new_stream, g_object_unref);
+}
+
+/**
+ * soup_multipart_input_stream_next_part_async:
+ * @multipart: the #SoupMultipartInputStream.
+ * @io_priority: the I/O priority for the request.
+ * @cancellable: a #GCancellable.
+ * @callback: callback to call when request is satisfied.
+ * @data: data for @callback
+ *
+ * Obtains a #GInputStream for the next request. See
+ * soup_multipart_input_stream_next_part() for details on the
+ * workflow.
+ *
+ * Since: 2.40
+ */
+void
+soup_multipart_input_stream_next_part_async (SoupMultipartInputStream *multipart,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data)
+{
+ GInputStream *stream = G_INPUT_STREAM (multipart);
+ GTask *task;
+ GError *error = NULL;
+
+ g_return_if_fail (SOUP_IS_MULTIPART_INPUT_STREAM (multipart));
+
+ task = g_task_new (multipart, cancellable, callback, data);
+ g_task_set_priority (task, io_priority);
+
+ if (!g_input_stream_set_pending (stream, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_run_in_thread (task, soup_multipart_input_stream_next_part_thread);
+ g_object_unref (task);
+}
+
+/**
+ * soup_multipart_input_stream_next_part_finish:
+ * @multipart: a #SoupMultipartInputStream.
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store any error, or NULL to ignore.
+ *
+ * Finishes an asynchronous request for the next part.
+ *
+ * Return value: (transfer full): a newly created #GInputStream for
+ * reading the next part or %NULL if there are no more parts.
+ *
+ * Since: 2.40
+ */
+GInputStream *
+soup_multipart_input_stream_next_part_finish (SoupMultipartInputStream *multipart,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, multipart), FALSE);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+/**
+ * soup_multipart_input_stream_get_headers:
+ * @multipart: a #SoupMultipartInputStream.
+ *
+ * Obtains the headers for the part currently being processed. Note
+ * that the #SoupMessageHeaders that are returned are owned by the
+ * #SoupMultipartInputStream and will be replaced when a call is made
+ * to soup_multipart_input_stream_next_part() or its async
+ * counterpart, so if keeping the headers is required, a copy must be
+ * made.
+ *
+ * Note that if a part had no headers at all an empty #SoupMessageHeaders
+ * will be returned.
+ *
+ * Return value: (transfer none): a #SoupMessageHeaders containing the headers
+ * for the part currently being processed or %NULL if the headers failed to
+ * parse.
+ *
+ * Since: 2.40
+ */
+SoupMessageHeaders *
+soup_multipart_input_stream_get_headers (SoupMultipartInputStream *multipart)
+{
+ return multipart->priv->current_headers;
+}
diff --git a/libsoup/soup-multipart-input-stream.h b/libsoup/soup-multipart-input-stream.h
new file mode 100644
index 00000000..8b735063
--- /dev/null
+++ b/libsoup/soup-multipart-input-stream.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012 Collabora Ltd.
+ */
+
+#ifndef SOUP_MULTIPART_INPUT_STREAM_H
+#define SOUP_MULTIPART_INPUT_STREAM_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-headers.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_MULTIPART_INPUT_STREAM (soup_multipart_input_stream_get_type ())
+#define SOUP_MULTIPART_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SOUP_TYPE_MULTIPART_INPUT_STREAM, SoupMultipartInputStream))
+#define SOUP_MULTIPART_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SOUP_TYPE_MULTIPART_INPUT_STREAM, SoupMultipartInputStreamClass))
+#define SOUP_IS_MULTIPART_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SOUP_TYPE_MULTIPART_INPUT_STREAM))
+#define SOUP_IS_MULTIPART_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SOUP_TYPE_MULTIPART_INPUT_STREAM))
+#define SOUP_MULTIPART_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SOUP_TYPE_MULTIPART_INPUT_STREAM, SoupMultipartInputStreamClass))
+
+typedef struct _SoupMultipartInputStream SoupMultipartInputStream;
+typedef struct _SoupMultipartInputStreamPrivate SoupMultipartInputStreamPrivate;
+typedef struct _SoupMultipartInputStreamClass SoupMultipartInputStreamClass;
+
+struct _SoupMultipartInputStream {
+ GFilterInputStream parent_instance;
+
+ /*< private >*/
+ SoupMultipartInputStreamPrivate *priv;
+};
+
+struct _SoupMultipartInputStreamClass {
+ GFilterInputStreamClass parent_class;
+};
+
+SOUP_AVAILABLE_IN_2_40
+GType soup_multipart_input_stream_get_type (void) G_GNUC_CONST;
+
+SOUP_AVAILABLE_IN_2_40
+SoupMultipartInputStream *soup_multipart_input_stream_new (SoupMessage *msg,
+ GInputStream *base_stream);
+
+SOUP_AVAILABLE_IN_2_40
+GInputStream *soup_multipart_input_stream_next_part (SoupMultipartInputStream *multipart,
+ GCancellable *cancellable,
+ GError **error);
+
+SOUP_AVAILABLE_IN_2_40
+void soup_multipart_input_stream_next_part_async (SoupMultipartInputStream *multipart,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+
+SOUP_AVAILABLE_IN_2_40
+GInputStream *soup_multipart_input_stream_next_part_finish (SoupMultipartInputStream *multipart,
+ GAsyncResult *result,
+ GError **error);
+
+SOUP_AVAILABLE_IN_2_40
+SoupMessageHeaders *soup_multipart_input_stream_get_headers (SoupMultipartInputStream *multipart);
+
+
+G_END_DECLS
+
+#endif /* SOUP_MULTIPART_INPUT_STREAM_H */
diff --git a/libsoup/soup-multipart.h b/libsoup/soup-multipart.h
index cc01eec6..ef2e36c7 100644
--- a/libsoup/soup-multipart.h
+++ b/libsoup/soup-multipart.h
@@ -17,33 +17,42 @@ typedef struct SoupMultipart SoupMultipart;
GType soup_multipart_get_type (void);
#define SOUP_TYPE_MULTIPART (soup_multipart_get_type ())
+SOUP_AVAILABLE_IN_2_26
SoupMultipart *soup_multipart_new (const char *mime_type);
+SOUP_AVAILABLE_IN_2_26
SoupMultipart *soup_multipart_new_from_message (SoupMessageHeaders *headers,
SoupMessageBody *body);
+SOUP_AVAILABLE_IN_2_26
int soup_multipart_get_length (SoupMultipart *multipart);
+SOUP_AVAILABLE_IN_2_26
gboolean soup_multipart_get_part (SoupMultipart *multipart,
int part,
SoupMessageHeaders **headers,
SoupBuffer **body);
+SOUP_AVAILABLE_IN_2_26
void soup_multipart_append_part (SoupMultipart *multipart,
SoupMessageHeaders *headers,
SoupBuffer *body);
+SOUP_AVAILABLE_IN_2_26
void soup_multipart_append_form_string (SoupMultipart *multipart,
const char *control_name,
const char *data);
+SOUP_AVAILABLE_IN_2_26
void soup_multipart_append_form_file (SoupMultipart *multipart,
const char *control_name,
const char *filename,
const char *content_type,
SoupBuffer *body);
+SOUP_AVAILABLE_IN_2_26
void soup_multipart_to_message (SoupMultipart *multipart,
SoupMessageHeaders *dest_headers,
SoupMessageBody *dest_body);
+SOUP_AVAILABLE_IN_2_26
void soup_multipart_free (SoupMultipart *multipart);
G_END_DECLS
diff --git a/libsoup/soup-password-manager-gnome.c b/libsoup/soup-password-manager-gnome.c
index 475cb594..8101683d 100644
--- a/libsoup/soup-password-manager-gnome.c
+++ b/libsoup/soup-password-manager-gnome.c
@@ -5,216 +5,24 @@
* Copyright (C) 2008 Red Hat, Inc.
*/
+/* This is just a stub now; eventually it will go away completely. */
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
-#include <gnome-keyring.h>
-
-#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-
#include "soup-password-manager-gnome.h"
#include "soup.h"
-static void soup_password_manager_gnome_interface_init (SoupPasswordManagerInterface *password_manager_interface);
-
G_DEFINE_TYPE_EXTENDED (SoupPasswordManagerGNOME, soup_password_manager_gnome, G_TYPE_OBJECT, 0,
- G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL)
- G_IMPLEMENT_INTERFACE (SOUP_TYPE_PASSWORD_MANAGER, soup_password_manager_gnome_interface_init))
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL))
static void
soup_password_manager_gnome_init (SoupPasswordManagerGNOME *manager_gnome)
{
}
-
-static void
-save_password_callback (GnomeKeyringResult result, guint32 val, gpointer data)
-{
-}
-
-static void
-async_save_password (SoupAuth *auth, const char *username,
- const char *password, gpointer user_data)
-{
- SoupURI *uri = user_data;
-
- gnome_keyring_set_network_password (
- NULL, /* use default keyring */
- username,
- soup_auth_get_realm (auth),
- uri->host,
- NULL,
- uri->scheme,
- soup_auth_get_scheme_name (auth),
- uri->port,
- password,
- save_password_callback, NULL, NULL);
-}
-
-static void
-sync_save_password (SoupAuth *auth, const char *username,
- const char *password, gpointer user_data)
-{
- SoupURI *uri = user_data;
- guint32 item_id;
-
- gnome_keyring_set_network_password_sync (
- NULL, /* use default keyring */
- username,
- soup_auth_get_realm (auth),
- uri->host,
- NULL,
- uri->scheme,
- soup_auth_get_scheme_name (auth),
- uri->port,
- password,
- &item_id);
-}
-
-static void
-update_auth_for_passwords (SoupAuth *auth, SoupMessage *msg,
- GList *passwords, gboolean async)
-{
- GnomeKeyringNetworkPasswordData *pdata;
- SoupURI *uri;
-
- while (passwords) {
- pdata = passwords->data;
- soup_auth_has_saved_password (auth, pdata->user,
- pdata->password);
- passwords = passwords->next;
- }
-
- uri = g_object_get_data (G_OBJECT (auth),
- "SoupPasswordManagerGNOME-save_password-uri");
- if (uri) {
- g_signal_handlers_disconnect_by_func (auth, async_save_password, uri);
- g_signal_handlers_disconnect_by_func (auth, sync_save_password, uri);
- }
-
- uri = soup_uri_copy (soup_message_get_uri (msg));
- g_signal_connect (auth, "save_password",
- G_CALLBACK (async ? async_save_password : sync_save_password),
- uri);
- g_object_set_data_full (G_OBJECT (auth),
- "SoupPasswordManagerGNOME-save_password-uri",
- uri, (GDestroyNotify)soup_uri_free);
-}
-
-typedef struct {
- SoupPasswordManager *password_manager;
- SoupMessage *msg;
- SoupAuth *auth;
- gboolean retrying;
-
- SoupPasswordManagerCallback callback;
- gpointer user_data;
-
- gpointer request;
-} SoupPasswordManagerGNOMEAuthData;
-
-static void
-find_password_callback (GnomeKeyringResult result, GList *list,
- gpointer user_data)
-{
- SoupPasswordManagerGNOMEAuthData *auth_data = user_data;
-
- /* FIXME: check result? */
-
- update_auth_for_passwords (auth_data->auth, auth_data->msg, list, TRUE);
- auth_data->callback (auth_data->password_manager,
- auth_data->msg, auth_data->auth,
- auth_data->retrying, auth_data->user_data);
-
- /* gnome-keyring will call free_auth_data to clean up for us. */
-}
-
-static void
-free_auth_data (gpointer data)
-{
- SoupPasswordManagerGNOMEAuthData *auth_data = data;
-
- g_object_unref (auth_data->auth);
- g_object_unref (auth_data->msg);
- g_slice_free (SoupPasswordManagerGNOMEAuthData, auth_data);
-}
-
-static void
-soup_password_manager_gnome_get_passwords_async (SoupPasswordManager *password_manager,
- SoupMessage *msg,
- SoupAuth *auth,
- gboolean retrying,
- GMainContext *async_context,
- GCancellable *cancellable,
- SoupPasswordManagerCallback callback,
- gpointer user_data)
-{
- SoupPasswordManagerGNOMEAuthData *auth_data;
- SoupURI *uri = soup_message_get_uri (msg);
-
- auth_data = g_slice_new (SoupPasswordManagerGNOMEAuthData);
- auth_data->password_manager = password_manager;
- auth_data->msg = g_object_ref (msg);
- auth_data->auth = g_object_ref (auth);
- auth_data->retrying = retrying;
-
- /* FIXME: async_context, cancellable */
-
- auth_data->callback = callback;
- auth_data->user_data = user_data;
-
- /* FIXME: should we be specifying protocol and port here, or
- * leaving them NULL/0 and filtering results in the callback?
- * We don't want to send https passwords to http, but the
- * reverse might be OK (if that's how other clients tend to
- * behave).
- */
- auth_data->request = gnome_keyring_find_network_password (
- NULL, /* user -- accept any */
- soup_auth_get_realm (auth), /* domain */
- uri->host, /* server */
- NULL, /* object -- unused */
- uri->scheme, /* protocol */
- soup_auth_get_scheme_name (auth), /* authtype */
- uri->port, /* port */
- find_password_callback, auth_data, free_auth_data);
-}
-
-static void
-soup_password_manager_gnome_get_passwords_sync (SoupPasswordManager *password_manager,
- SoupMessage *msg,
- SoupAuth *auth,
- GCancellable *cancellable)
-{
- SoupURI *uri = soup_message_get_uri (msg);
- GList *results = NULL;
-
- /* FIXME: cancellable */
-
- gnome_keyring_find_network_password_sync (
- NULL, /* user -- accept any */
- soup_auth_get_realm (auth), /* domain */
- uri->host, /* server */
- NULL, /* object -- unused */
- uri->scheme, /* protocol */
- soup_auth_get_scheme_name (auth), /* authtype */
- uri->port, /* port */
- &results);
-
- update_auth_for_passwords (auth, msg, results, FALSE);
-}
-
static void
soup_password_manager_gnome_class_init (SoupPasswordManagerGNOMEClass *gnome_class)
{
}
-
-static void
-soup_password_manager_gnome_interface_init (SoupPasswordManagerInterface *password_manager_interface)
-{
- password_manager_interface->get_passwords_async =
- soup_password_manager_gnome_get_passwords_async;
- password_manager_interface->get_passwords_sync =
- soup_password_manager_gnome_get_passwords_sync;
-}
diff --git a/libsoup/soup-password-manager.c b/libsoup/soup-password-manager.c
index 3914179f..532ff7eb 100644
--- a/libsoup/soup-password-manager.c
+++ b/libsoup/soup-password-manager.c
@@ -9,8 +9,6 @@
#include <config.h>
#endif
-#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-
#include "soup-password-manager.h"
#include "soup.h"
@@ -25,27 +23,8 @@ soup_password_manager_default_init (SoupPasswordManagerInterface *iface)
/**
* soup_password_manager_get_passwords_async:
- * @password_manager: the #SoupPasswordManager
- * @msg: the #SoupMessage being authenticated
- * @auth: the #SoupAuth being authenticated
- * @retrying: whether or not this is a re-attempt to authenticate
- * @async_context: (allow-none): the #GMainContext to invoke @callback in
- * @cancellable: a #GCancellable, or %NULL
- * @callback: callback to invoke after fetching passwords
- * @user_data: data for @callback
- *
- * Asynchronously attempts to look up saved passwords for @auth/@msg
- * and then calls @callback after updating @auth with the information.
- * Also registers @auth with @password_manager so that if the caller
- * calls soup_auth_save_password() on it, the password will be saved.
- *
- * #SoupPasswordManager does not actually use the @retrying flag itself;
- * it just passes its value on to @callback.
- *
- * If @cancellable is cancelled, @callback will still be invoked.
- *
- * Since: 2.28
- **/
+ * @callback: (scope async)
+ */
void
soup_password_manager_get_passwords_async (SoupPasswordManager *password_manager,
SoupMessage *msg,
@@ -56,32 +35,14 @@ soup_password_manager_get_passwords_async (SoupPasswordManager *password_manage
SoupPasswordManagerCallback callback,
gpointer user_data)
{
- SOUP_PASSWORD_MANAGER_GET_CLASS (password_manager)->
- get_passwords_async (password_manager, msg, auth, retrying,
- async_context, cancellable,
- callback, user_data);
+ g_warn_if_reached ();
}
-/**
- * soup_password_manager_get_passwords_sync:
- * @password_manager: the #SoupPasswordManager
- * @msg: the #SoupMessage being authenticated
- * @auth: the #SoupAuth being authenticated
- * @cancellable: a #GCancellable, or %NULL
- *
- * Synchronously attempts to look up saved passwords for @auth/@msg
- * and updates @auth with the information. Also registers @auth with
- * @password_manager so that if the caller calls
- * soup_auth_save_password() on it, the password will be saved.
- *
- * Since: 2.28
- **/
void
soup_password_manager_get_passwords_sync (SoupPasswordManager *password_manager,
SoupMessage *msg,
SoupAuth *auth,
GCancellable *cancellable)
{
- SOUP_PASSWORD_MANAGER_GET_CLASS (password_manager)->
- get_passwords_sync (password_manager, msg, auth, cancellable);
+ g_warn_if_reached ();
}
diff --git a/libsoup/soup-password-manager.h b/libsoup/soup-password-manager.h
index 775f84c5..aa5faf55 100644
--- a/libsoup/soup-password-manager.h
+++ b/libsoup/soup-password-manager.h
@@ -6,8 +6,6 @@
#ifndef SOUP_PASSWORD_MANAGER_H
#define SOUP_PASSWORD_MANAGER_H 1
-#ifdef LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-
#include <libsoup/soup-types.h>
#define SOUP_TYPE_PASSWORD_MANAGER (soup_password_manager_get_type ())
@@ -19,8 +17,8 @@
typedef struct _SoupPasswordManager SoupPasswordManager;
-typedef void (*SoupPasswordManagerCallback) (SoupPasswordManager *,
- SoupMessage *, SoupAuth *,
+typedef void (*SoupPasswordManagerCallback) (SoupPasswordManager *password_manager,
+ SoupMessage *msg, SoupAuth *auth,
gboolean retrying,
gpointer user_data);
@@ -28,17 +26,25 @@ typedef struct {
GTypeInterface base;
/* virtual methods */
- void (*get_passwords_async) (SoupPasswordManager *, SoupMessage *,
- SoupAuth *, gboolean,
- GMainContext *, GCancellable *,
- SoupPasswordManagerCallback, gpointer);
- void (*get_passwords_sync) (SoupPasswordManager *, SoupMessage *,
- SoupAuth *, GCancellable *);
+ void (*get_passwords_async) (SoupPasswordManager *password_manager,
+ SoupMessage *msg, SoupAuth *auth,
+ gboolean retrying,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupPasswordManagerCallback callback,
+ gpointer user_data);
+ void (*get_passwords_sync) (SoupPasswordManager *password_manager,
+ SoupMessage *msg, SoupAuth *auth,
+ GCancellable *cancellable);
} SoupPasswordManagerInterface;
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
GType soup_password_manager_get_type (void);
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
void soup_password_manager_get_passwords_async (SoupPasswordManager *password_manager,
SoupMessage *msg,
SoupAuth *auth,
@@ -48,11 +54,11 @@ void soup_password_manager_get_passwords_async (SoupPasswordManager *password_
SoupPasswordManagerCallback callback,
gpointer user_data);
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_28
void soup_password_manager_get_passwords_sync (SoupPasswordManager *password_manager,
SoupMessage *msg,
SoupAuth *auth,
GCancellable *cancellable);
-#endif /* LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY */
-
#endif /* SOUP_PASSWORD_MANAGER_H */
diff --git a/libsoup/soup-proxy-resolver-default.c b/libsoup/soup-proxy-resolver-default.c
index 76e9ff6d..7fd09349 100644
--- a/libsoup/soup-proxy-resolver-default.c
+++ b/libsoup/soup-proxy-resolver-default.c
@@ -9,16 +9,29 @@
#include <config.h>
#endif
+#undef SOUP_VERSION_MIN_REQUIRED
+#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_42
+
#include "soup-proxy-resolver-default.h"
#include "soup.h"
/**
- * SoupProxyResolverDefault:
+ * SECTION:soup-proxy-resolver-default
+ * @short_description: System proxy configuration integration
+ *
+ * #SoupProxyResolverDefault is a <type>SoupProxyURIResolver</type>
+ * implementation that uses the default gio #GProxyResolver to resolve
+ * proxies.
*
- * A #SoupProxyURIResolver implementation that uses the default gio
- * #GProxyResolver to resolve proxies.
+ * In libsoup 2.44 and later, you can set the session's
+ * #SoupSession:proxy-resolver property to the resolver returned by
+ * g_proxy_resolver_get_default() to get the same effect. Note that
+ * for "plain" #SoupSessions (ie, not #SoupSessionAsync or
+ * #SoupSessionSync), this is done for you automatically.
*
* Since: 2.34
+ *
+ * Deprecated: Use #SoupSession:proxy-resolver
*/
static void soup_proxy_resolver_default_interface_init (SoupProxyURIResolverInterface *proxy_resolver_interface);
diff --git a/libsoup/soup-proxy-resolver-gnome.c b/libsoup/soup-proxy-resolver-gnome.c
index bc423370..16a34680 100644
--- a/libsoup/soup-proxy-resolver-gnome.c
+++ b/libsoup/soup-proxy-resolver-gnome.c
@@ -9,6 +9,9 @@
#include <config.h>
#endif
+#undef SOUP_VERSION_MIN_REQUIRED
+#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_40
+
#include <string.h>
#include "soup-proxy-resolver-gnome.h"
diff --git a/libsoup/soup-proxy-resolver-static.c b/libsoup/soup-proxy-resolver-static.c
deleted file mode 100644
index 94327671..00000000
--- a/libsoup/soup-proxy-resolver-static.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-proxy-resolver-static.c: Static proxy "resolution"
- *
- * Copyright (C) 2008 Red Hat, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "soup-proxy-resolver-static.h"
-#include "soup.h"
-
-typedef struct {
- SoupURI *proxy_uri;
-
-} SoupProxyResolverStaticPrivate;
-#define SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStaticPrivate))
-
-static void soup_proxy_resolver_static_interface_init (SoupProxyURIResolverInterface *proxy_resolver_interface);
-
-G_DEFINE_TYPE_EXTENDED (SoupProxyResolverStatic, soup_proxy_resolver_static, G_TYPE_OBJECT, 0,
- G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL)
- G_IMPLEMENT_INTERFACE (SOUP_TYPE_PROXY_URI_RESOLVER, soup_proxy_resolver_static_interface_init))
-
-enum {
- PROP_0,
-
- PROP_PROXY_URI,
-
- LAST_PROP
-};
-
-static void
-soup_proxy_resolver_static_init (SoupProxyResolverStatic *resolver_static)
-{
-}
-
-static void
-soup_proxy_resolver_static_finalize (GObject *object)
-{
- SoupProxyResolverStaticPrivate *priv =
- SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (object);
-
- g_clear_pointer (&priv->proxy_uri, soup_uri_free);
-
- G_OBJECT_CLASS (soup_proxy_resolver_static_parent_class)->finalize (object);
-}
-
-static void
-soup_proxy_resolver_static_set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- SoupProxyResolverStaticPrivate *priv =
- SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (object);
- SoupURI *uri;
-
- switch (prop_id) {
- case PROP_PROXY_URI:
- uri = g_value_get_boxed (value);
- if (priv->proxy_uri)
- soup_uri_free (priv->proxy_uri);
-
- priv->proxy_uri = uri ? soup_uri_copy (uri) : NULL;
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-soup_proxy_resolver_static_get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- SoupProxyResolverStaticPrivate *priv =
- SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (object);
-
- switch (prop_id) {
- case PROP_PROXY_URI:
- g_value_set_boxed (value, priv->proxy_uri);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-typedef struct {
- SoupProxyURIResolver *proxy_resolver;
- SoupProxyURIResolverCallback callback;
- gpointer user_data;
-} SoupStaticAsyncData;
-
-static gboolean
-idle_return_proxy_uri (gpointer data)
-{
- SoupStaticAsyncData *ssad = data;
- SoupProxyResolverStaticPrivate *priv =
- SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (ssad->proxy_resolver);
-
- ssad->callback (ssad->proxy_resolver,
- SOUP_STATUS_OK, priv->proxy_uri,
- ssad->user_data);
- g_object_unref (ssad->proxy_resolver);
- g_slice_free (SoupStaticAsyncData, ssad);
-
- return FALSE;
-}
-
-static void
-soup_proxy_resolver_static_get_proxy_uri_async (SoupProxyURIResolver *proxy_resolver,
- SoupURI *uri,
- GMainContext *async_context,
- GCancellable *cancellable,
- SoupProxyURIResolverCallback callback,
- gpointer user_data)
-{
- SoupStaticAsyncData *ssad;
-
- ssad = g_slice_new0 (SoupStaticAsyncData);
- ssad->proxy_resolver = g_object_ref (proxy_resolver);
- ssad->callback = callback;
- ssad->user_data = user_data;
- soup_add_completion (async_context, idle_return_proxy_uri, ssad);
-}
-
-static guint
-soup_proxy_resolver_static_get_proxy_uri_sync (SoupProxyURIResolver *proxy_resolver,
- SoupURI *uri,
- GCancellable *cancellable,
- SoupURI **proxy_uri)
-{
- SoupProxyResolverStaticPrivate *priv =
- SOUP_PROXY_RESOLVER_STATIC_GET_PRIVATE (proxy_resolver);
-
- *proxy_uri = soup_uri_copy (priv->proxy_uri);
- return SOUP_STATUS_OK;
-}
-
-static void
-soup_proxy_resolver_static_class_init (SoupProxyResolverStaticClass *static_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (static_class);
-
- g_type_class_add_private (static_class, sizeof (SoupProxyResolverStaticPrivate));
-
- object_class->set_property = soup_proxy_resolver_static_set_property;
- object_class->get_property = soup_proxy_resolver_static_get_property;
- object_class->finalize = soup_proxy_resolver_static_finalize;
-
- g_object_class_install_property (
- object_class, PROP_PROXY_URI,
- g_param_spec_boxed (SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
- "Proxy URI",
- "The HTTP Proxy to use",
- SOUP_TYPE_URI,
- G_PARAM_READWRITE));
-}
-
-static void
-soup_proxy_resolver_static_interface_init (SoupProxyURIResolverInterface *proxy_uri_resolver_interface)
-{
- proxy_uri_resolver_interface->get_proxy_uri_async =
- soup_proxy_resolver_static_get_proxy_uri_async;
- proxy_uri_resolver_interface->get_proxy_uri_sync =
- soup_proxy_resolver_static_get_proxy_uri_sync;
-}
-
-SoupProxyURIResolver *
-soup_proxy_resolver_static_new (SoupURI *proxy_uri)
-{
- return g_object_new (SOUP_TYPE_PROXY_RESOLVER_STATIC,
- SOUP_PROXY_RESOLVER_STATIC_PROXY_URI, proxy_uri,
- NULL);
-}
diff --git a/libsoup/soup-proxy-resolver-static.h b/libsoup/soup-proxy-resolver-static.h
deleted file mode 100644
index 090b84b9..00000000
--- a/libsoup/soup-proxy-resolver-static.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2008 Red Hat, Inc.
- */
-
-#ifndef SOUP_PROXY_RESOLVER_STATIC_H
-#define SOUP_PROXY_RESOLVER_STATIC_H 1
-
-#include "soup-proxy-uri-resolver.h"
-#include "soup-uri.h"
-
-#define SOUP_TYPE_PROXY_RESOLVER_STATIC (soup_proxy_resolver_static_get_type ())
-#define SOUP_PROXY_RESOLVER_STATIC(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStatic))
-#define SOUP_PROXY_RESOLVER_STATIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStaticClass))
-#define SOUP_IS_PROXY_RESOLVER_STATIC(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_RESOLVER_STATIC))
-#define SOUP_IS_PROXY_RESOLVER_STATIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_RESOLVER_STATIC))
-#define SOUP_PROXY_RESOLVER_STATIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_PROXY_RESOLVER_STATIC, SoupProxyResolverStaticClass))
-
-typedef struct {
- GObject parent;
-
-} SoupProxyResolverStatic;
-
-typedef struct {
- GObjectClass parent_class;
-
-} SoupProxyResolverStaticClass;
-
-GType soup_proxy_resolver_static_get_type (void);
-
-#define SOUP_PROXY_RESOLVER_STATIC_PROXY_URI "proxy-uri"
-
-SoupProxyURIResolver *soup_proxy_resolver_static_new (SoupURI *proxy_uri);
-
-#endif /* SOUP_PROXY_RESOLVER_STATIC_H */
diff --git a/libsoup/soup-proxy-resolver-wrapper.c b/libsoup/soup-proxy-resolver-wrapper.c
new file mode 100644
index 00000000..e07664ca
--- /dev/null
+++ b/libsoup/soup-proxy-resolver-wrapper.c
@@ -0,0 +1,167 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-proxy-resolver-wrapper.c: SoupProxyURIResolver -> GProxyResolver wrapper
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-proxy-resolver-wrapper.h"
+#include "soup.h"
+
+static void soup_proxy_resolver_wrapper_interface_init (GProxyResolverInterface *proxy_resolver_interface);
+
+G_DEFINE_TYPE_WITH_CODE (SoupProxyResolverWrapper, soup_proxy_resolver_wrapper, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_PROXY_RESOLVER, soup_proxy_resolver_wrapper_interface_init);
+ )
+
+static void
+soup_proxy_resolver_wrapper_init (SoupProxyResolverWrapper *resolver_wrapper)
+{
+}
+
+static void
+soup_proxy_resolver_wrapper_finalize (GObject *object)
+{
+ SoupProxyResolverWrapper *wrapper =
+ SOUP_PROXY_RESOLVER_WRAPPER (object);
+
+ g_clear_object (&wrapper->soup_resolver);
+
+ G_OBJECT_CLASS (soup_proxy_resolver_wrapper_parent_class)->finalize (object);
+}
+
+static char **
+convert_response (SoupURI *source_uri, guint status,
+ SoupURI *proxy_uri, GError **error)
+{
+ char **proxies = NULL;
+
+ if (status == SOUP_STATUS_CANT_RESOLVE_PROXY) {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
+ "%s (%s)", soup_status_get_phrase (status),
+ source_uri->host);
+ } else if (status == SOUP_STATUS_CANT_CONNECT_PROXY) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
+ "%s (%s)", soup_status_get_phrase (status),
+ source_uri->host);
+ } else {
+ g_return_val_if_fail (status == SOUP_STATUS_OK, NULL);
+
+ proxies = g_new (char *, 2);
+ proxies[0] = soup_uri_to_string (proxy_uri, FALSE);
+ proxies[1] = NULL;
+
+ soup_uri_free (proxy_uri);
+ }
+
+ return proxies;
+}
+
+static void
+wrapper_lookup_async_complete (SoupProxyURIResolver *resolver,
+ guint status, SoupURI *proxy_uri,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ SoupURI *source_uri = g_task_get_task_data (task);
+ char **proxies;
+ GError *error = NULL;
+
+ proxies = convert_response (source_uri, status, proxy_uri, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, proxies, (GDestroyNotify) g_strfreev);
+ g_object_unref (task);
+}
+
+static void
+soup_proxy_resolver_wrapper_lookup_async (GProxyResolver *resolver,
+ const gchar *uri,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupProxyResolverWrapper *wrapper =
+ SOUP_PROXY_RESOLVER_WRAPPER (resolver);
+ GTask *task;
+ SoupURI *source_uri;
+
+ task = g_task_new (resolver, cancellable, callback, user_data);
+ source_uri = soup_uri_new (uri);
+ g_task_set_task_data (task, source_uri, (GDestroyNotify) soup_uri_free);
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ soup_proxy_uri_resolver_get_proxy_uri_async (wrapper->soup_resolver,
+ source_uri,
+ g_main_context_get_thread_default (),
+ cancellable,
+ wrapper_lookup_async_complete,
+ task);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+}
+
+static char **
+soup_proxy_resolver_wrapper_lookup_finish (GProxyResolver *resolver,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static gchar **
+soup_proxy_resolver_wrapper_lookup (GProxyResolver *resolver,
+ const gchar *uri,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupProxyResolverWrapper *wrapper =
+ SOUP_PROXY_RESOLVER_WRAPPER (resolver);
+ SoupURI *source_uri, *proxy_uri;
+ guint status;
+ gchar **proxies;
+
+ source_uri = soup_uri_new (uri);
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ status = soup_proxy_uri_resolver_get_proxy_uri_sync (wrapper->soup_resolver,
+ source_uri,
+ cancellable,
+ &proxy_uri);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+ proxies = convert_response (source_uri, status, proxy_uri, error);
+ soup_uri_free (source_uri);
+ return proxies;
+}
+
+static void
+soup_proxy_resolver_wrapper_class_init (SoupProxyResolverWrapperClass *static_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (static_class);
+
+ object_class->finalize = soup_proxy_resolver_wrapper_finalize;
+}
+
+static void
+soup_proxy_resolver_wrapper_interface_init (GProxyResolverInterface *proxy_resolver_interface)
+{
+ proxy_resolver_interface->lookup =
+ soup_proxy_resolver_wrapper_lookup;
+ proxy_resolver_interface->lookup_async =
+ soup_proxy_resolver_wrapper_lookup_async;
+ proxy_resolver_interface->lookup_finish =
+ soup_proxy_resolver_wrapper_lookup_finish;
+}
+
+GProxyResolver *
+soup_proxy_resolver_wrapper_new (SoupProxyURIResolver *soup_resolver)
+{
+ SoupProxyResolverWrapper *wrapper;
+
+ wrapper = g_object_new (SOUP_TYPE_PROXY_RESOLVER_WRAPPER, NULL);
+ wrapper->soup_resolver = g_object_ref (soup_resolver);
+ return (GProxyResolver *)wrapper;
+}
diff --git a/libsoup/soup-proxy-resolver-wrapper.h b/libsoup/soup-proxy-resolver-wrapper.h
new file mode 100644
index 00000000..d16b57c9
--- /dev/null
+++ b/libsoup/soup-proxy-resolver-wrapper.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PROXY_RESOLVER_WRAPPER_H
+#define SOUP_PROXY_RESOLVER_WRAPPER_H 1
+
+#include "soup-proxy-uri-resolver.h"
+#include "soup-uri.h"
+
+#define SOUP_TYPE_PROXY_RESOLVER_WRAPPER (soup_proxy_resolver_wrapper_get_type ())
+#define SOUP_PROXY_RESOLVER_WRAPPER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER_WRAPPER, SoupProxyResolverWrapper))
+#define SOUP_PROXY_RESOLVER_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_RESOLVER_WRAPPER, SoupProxyResolverWrapperClass))
+#define SOUP_IS_PROXY_RESOLVER_WRAPPER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_RESOLVER_WRAPPER))
+#define SOUP_IS_PROXY_RESOLVER_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_RESOLVER_WRAPPER))
+#define SOUP_PROXY_RESOLVER_WRAPPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_PROXY_RESOLVER_WRAPPER, SoupProxyResolverWrapperClass))
+
+typedef struct {
+ GObject parent;
+
+ SoupProxyURIResolver *soup_resolver;
+} SoupProxyResolverWrapper;
+
+typedef struct {
+ GObjectClass parent_class;
+
+} SoupProxyResolverWrapperClass;
+
+GType soup_proxy_resolver_wrapper_get_type (void);
+
+GProxyResolver *soup_proxy_resolver_wrapper_new (SoupProxyURIResolver *soup_resolver);
+
+#endif /* SOUP_PROXY_RESOLVER_WRAPPER_H */
diff --git a/libsoup/soup-proxy-resolver.c b/libsoup/soup-proxy-resolver.c
index 8092175f..a84c27ab 100644
--- a/libsoup/soup-proxy-resolver.c
+++ b/libsoup/soup-proxy-resolver.c
@@ -13,46 +13,14 @@
#include "soup.h"
static void soup_proxy_resolver_default_init (SoupProxyResolverInterface *iface);
-static void soup_proxy_resolver_uri_resolver_interface_init (SoupProxyURIResolverInterface *uri_resolver_interface);
G_DEFINE_INTERFACE_WITH_CODE (SoupProxyResolver, soup_proxy_resolver, G_TYPE_OBJECT,
g_type_interface_add_prerequisite (g_define_type_id, SOUP_TYPE_SESSION_FEATURE);
)
static void
-proxy_resolver_interface_check (gpointer func_data, gpointer g_iface)
-{
- GTypeInterface *iface = g_iface;
-
- if (iface->g_type != SOUP_TYPE_PROXY_RESOLVER)
- return;
-
- /* If the class hasn't already declared that it implements
- * SoupProxyURIResolver, add our own compat implementation.
- */
- if (!g_type_is_a (iface->g_instance_type, SOUP_TYPE_PROXY_URI_RESOLVER)) {
- const GInterfaceInfo uri_resolver_interface_info = {
- (GInterfaceInitFunc) soup_proxy_resolver_uri_resolver_interface_init, NULL, NULL
- };
- g_type_add_interface_static (iface->g_instance_type,
- SOUP_TYPE_PROXY_URI_RESOLVER,
- &uri_resolver_interface_info);
- }
-}
-
-
-static void
soup_proxy_resolver_default_init (SoupProxyResolverInterface *iface)
{
- /* Add an interface_check where we can kludgily add the
- * SoupProxyURIResolver interface to all SoupProxyResolvers.
- * (SoupProxyResolver can't just implement
- * SoupProxyURIResolver itself because interface types can't
- * implement other interfaces.) This is an ugly hack, but it
- * only gets used if someone actually creates a
- * SoupProxyResolver...
- */
- g_type_add_interface_check (NULL, proxy_resolver_interface_check);
}
void
@@ -63,10 +31,16 @@ soup_proxy_resolver_get_proxy_async (SoupProxyResolver *proxy_resolver,
SoupProxyResolverCallback callback,
gpointer user_data)
{
+#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#endif
SOUP_PROXY_RESOLVER_GET_CLASS (proxy_resolver)->
get_proxy_async (proxy_resolver, msg,
async_context, cancellable,
callback, user_data);
+#ifdef G_GNUC_END_IGNORE_DEPRECATIONS
+G_GNUC_END_IGNORE_DEPRECATIONS
+#endif
}
guint
@@ -75,94 +49,12 @@ soup_proxy_resolver_get_proxy_sync (SoupProxyResolver *proxy_resolver,
GCancellable *cancellable,
SoupAddress **addr)
{
+#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#endif
return SOUP_PROXY_RESOLVER_GET_CLASS (proxy_resolver)->
get_proxy_sync (proxy_resolver, msg, cancellable, addr);
-}
-
-/* SoupProxyURIResolver implementation */
-
-static SoupURI *
-uri_from_address (SoupAddress *addr)
-{
- SoupURI *proxy_uri;
-
- proxy_uri = soup_uri_new (NULL);
- soup_uri_set_scheme (proxy_uri, SOUP_URI_SCHEME_HTTP);
- soup_uri_set_host (proxy_uri, soup_address_get_name (addr));
- soup_uri_set_port (proxy_uri, soup_address_get_port (addr));
- soup_uri_set_path (proxy_uri, "");
- return proxy_uri;
-}
-
-typedef struct {
- SoupProxyURIResolverCallback callback;
- gpointer user_data;
-} ProxyURIResolverAsyncData;
-
-static void
-compat_got_proxy (SoupProxyResolver *proxy_resolver,
- SoupMessage *msg, guint status, SoupAddress *proxy_addr,
- gpointer user_data)
-{
- ProxyURIResolverAsyncData *purad = user_data;
- SoupURI *proxy_uri;
-
- proxy_uri = proxy_addr ? uri_from_address (proxy_addr) : NULL;
- purad->callback (SOUP_PROXY_URI_RESOLVER (proxy_resolver),
- status, proxy_uri, purad->user_data);
- g_object_unref (msg);
- if (proxy_uri)
- soup_uri_free (proxy_uri);
- g_slice_free (ProxyURIResolverAsyncData, purad);
-}
-
-static void
-compat_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
- SoupURI *uri, GMainContext *async_context,
- GCancellable *cancellable,
- SoupProxyURIResolverCallback callback,
- gpointer user_data)
-{
- SoupMessage *dummy_msg;
- ProxyURIResolverAsyncData *purad;
-
- dummy_msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
-
- purad = g_slice_new (ProxyURIResolverAsyncData);
- purad->callback = callback;
- purad->user_data = user_data;
-
- soup_proxy_resolver_get_proxy_async (
- SOUP_PROXY_RESOLVER (proxy_uri_resolver), dummy_msg,
- async_context, cancellable,
- compat_got_proxy, purad);
-}
-
-static guint
-compat_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
- SoupURI *uri, GCancellable *cancellable,
- SoupURI **proxy_uri)
-{
- SoupMessage *dummy_msg;
- SoupAddress *proxy_addr = NULL;
- guint status;
-
- dummy_msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
- status = soup_proxy_resolver_get_proxy_sync (
- SOUP_PROXY_RESOLVER (proxy_uri_resolver), dummy_msg,
- cancellable, &proxy_addr);
- g_object_unref (dummy_msg);
- if (!proxy_addr)
- return status;
-
- *proxy_uri = uri_from_address (proxy_addr);
- g_object_unref (proxy_addr);
- return status;
-}
-
-static void
-soup_proxy_resolver_uri_resolver_interface_init (SoupProxyURIResolverInterface *uri_resolver_interface)
-{
- uri_resolver_interface->get_proxy_uri_async = compat_get_proxy_uri_async;
- uri_resolver_interface->get_proxy_uri_sync = compat_get_proxy_uri_sync;
+#ifdef G_GNUC_END_IGNORE_DEPRECATIONS
+G_GNUC_END_IGNORE_DEPRECATIONS
+#endif
}
diff --git a/libsoup/soup-proxy-resolver.h b/libsoup/soup-proxy-resolver.h
index dd787143..72e48852 100644
--- a/libsoup/soup-proxy-resolver.h
+++ b/libsoup/soup-proxy-resolver.h
@@ -10,7 +10,7 @@
G_BEGIN_DECLS
-#ifndef LIBSOUP_DISABLE_DEPRECATED
+#ifndef SOUP_DISABLE_DEPRECATED
#define SOUP_TYPE_PROXY_RESOLVER (soup_proxy_resolver_get_type ())
#define SOUP_PROXY_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER, SoupProxyResolver))
@@ -39,14 +39,17 @@ typedef struct {
} SoupProxyResolverInterface;
+SOUP_DEPRECATED_IN_2_28
GType soup_proxy_resolver_get_type (void);
+SOUP_DEPRECATED_IN_2_28
void soup_proxy_resolver_get_proxy_async (SoupProxyResolver *proxy_resolver,
SoupMessage *msg,
GMainContext *async_context,
GCancellable *cancellable,
SoupProxyResolverCallback callback,
gpointer user_data);
+SOUP_DEPRECATED_IN_2_28
guint soup_proxy_resolver_get_proxy_sync (SoupProxyResolver *proxy_resolver,
SoupMessage *msg,
GCancellable *cancellable,
diff --git a/libsoup/soup-proxy-uri-resolver.c b/libsoup/soup-proxy-uri-resolver.c
index 8aecec47..7971fcb7 100644
--- a/libsoup/soup-proxy-uri-resolver.c
+++ b/libsoup/soup-proxy-uri-resolver.c
@@ -9,27 +9,48 @@
#include <config.h>
#endif
+#undef SOUP_VERSION_MIN_REQUIRED
+#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_42
+
#include "soup-proxy-uri-resolver.h"
#include "soup.h"
-G_DEFINE_INTERFACE (SoupProxyURIResolver, soup_proxy_uri_resolver, G_TYPE_OBJECT)
+/*
+ * SECTION:soup-proxy-uri-resolver
+ * @short_description: Interface for locating HTTP proxies
+ *
+ * #SoupProxyURIResolver is an interface for finding appropriate HTTP
+ * proxies to use.
+ *
+ * Deprecated: #SoupSession now has a #SoupSession:proxy-resolver
+ * property that takes a #GProxyResolver (which is semantically
+ * identical to #SoupProxyURIResolver).
+ *
+ * Even in older releases of libsoup, you are not likely to have to
+ * implement this interface on your own; instead, you should usually
+ * just be able to use #SoupProxyResolverDefault.
+ */
+
+G_DEFINE_INTERFACE_WITH_CODE (SoupProxyURIResolver, soup_proxy_uri_resolver, G_TYPE_OBJECT,
+ g_type_interface_add_prerequisite (g_define_type_id, SOUP_TYPE_SESSION_FEATURE);
+ )
static void
soup_proxy_uri_resolver_default_init (SoupProxyURIResolverInterface *iface)
{
}
-/**
+/*
* SoupProxyURIResolverCallback:
* @resolver: the #SoupProxyURIResolver
- * @status: a #SoupKnownStatusCode
+ * @status: a #SoupStatus
* @proxy_uri: the resolved proxy URI, or %NULL
* @user_data: data passed to soup_proxy_uri_resolver_get_proxy_uri_async()
*
* Callback for soup_proxy_uri_resolver_get_proxy_uri_async()
- **/
+ */
-/**
+/*
* soup_proxy_uri_resolver_get_proxy_uri_async:
* @proxy_uri_resolver: the #SoupProxyURIResolver
* @uri: the #SoupURI you want a proxy for
@@ -42,7 +63,10 @@ soup_proxy_uri_resolver_default_init (SoupProxyURIResolverInterface *iface)
* @callback.
*
* Since: 2.26.3
- **/
+ *
+ * Deprecated: #SoupProxyURIResolver is deprecated in favor of
+ * #GProxyResolver
+ */
void
soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
SoupURI *uri,
@@ -57,7 +81,7 @@ soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_re
callback, user_data);
}
-/**
+/*
* soup_proxy_uri_resolver_get_proxy_uri_sync:
* @proxy_uri_resolver: the #SoupProxyURIResolver
* @uri: the #SoupURI you want a proxy for
@@ -72,7 +96,10 @@ soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_re
* error.
*
* Since: 2.26.3
- **/
+ *
+ * Deprecated: #SoupProxyURIResolver is deprecated in favor of
+ * #GProxyResolver
+ */
guint
soup_proxy_uri_resolver_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
SoupURI *uri,
diff --git a/libsoup/soup-proxy-uri-resolver.h b/libsoup/soup-proxy-uri-resolver.h
index 1d15b8de..e203125b 100644
--- a/libsoup/soup-proxy-uri-resolver.h
+++ b/libsoup/soup-proxy-uri-resolver.h
@@ -43,14 +43,20 @@ typedef struct {
void (*_libsoup_reserved4) (void);
} SoupProxyURIResolverInterface;
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_44
GType soup_proxy_uri_resolver_get_type (void);
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_44
void soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
SoupURI *uri,
GMainContext *async_context,
GCancellable *cancellable,
SoupProxyURIResolverCallback callback,
gpointer user_data);
+SOUP_AVAILABLE_IN_2_28
+SOUP_DEPRECATED_IN_2_44
guint soup_proxy_uri_resolver_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
SoupURI *uri,
GCancellable *cancellable,
diff --git a/libsoup/soup-request-data.c b/libsoup/soup-request-data.c
index 933d7522..678e84da 100644
--- a/libsoup/soup-request-data.c
+++ b/libsoup/soup-request-data.c
@@ -25,14 +25,19 @@
#include <config.h>
#endif
-#include <glib/gi18n-lib.h>
-
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+#include <string.h>
#include "soup-request-data.h"
#include "soup.h"
#include "soup-misc-private.h"
+/**
+ * SECTION:soup-request-data
+ * @short_description: SoupRequest support for "data" URIs
+ *
+ * #SoupRequestData implements #SoupRequest for "data" URIs.
+ */
+
G_DEFINE_TYPE (SoupRequestData, soup_request_data, SOUP_TYPE_REQUEST)
struct _SoupRequestDataPrivate {
@@ -91,7 +96,7 @@ soup_request_data_send (SoupRequest *request,
end = comma;
if (end != start)
- data->priv->content_type = uri_decoded_copy (start, end - start);
+ data->priv->content_type = soup_uri_decoded_copy (start, end - start, NULL);
}
memstream = g_memory_input_stream_new ();
@@ -100,12 +105,14 @@ soup_request_data_send (SoupRequest *request,
start = comma + 1;
if (*start) {
- guchar *buf = (guchar *) soup_uri_decode (start);
+ int decoded_length = 0;
+ guchar *buf = (guchar *) soup_uri_decoded_copy (start, strlen (start),
+ &decoded_length);
if (base64)
buf = g_base64_decode_inplace ((gchar*) buf, &data->priv->content_length);
else
- data->priv->content_length = strlen ((const char *) buf);
+ data->priv->content_length = decoded_length;
g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream),
buf, data->priv->content_length,
diff --git a/libsoup/soup-request-data.h b/libsoup/soup-request-data.h
index aeb95995..929c5908 100644
--- a/libsoup/soup-request-data.h
+++ b/libsoup/soup-request-data.h
@@ -22,8 +22,6 @@
#ifndef SOUP_REQUEST_DATA_H
#define SOUP_REQUEST_DATA_H 1
-#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include "soup-request.h"
G_BEGIN_DECLS
@@ -47,10 +45,9 @@ typedef struct {
SoupRequestClass parent;
} SoupRequestDataClass;
+SOUP_AVAILABLE_IN_2_34
GType soup_request_data_get_type (void);
G_END_DECLS
-#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
-
#endif /* SOUP_REQUEST_DATA_H */
diff --git a/libsoup/soup-request-file.c b/libsoup/soup-request-file.c
index 0b5638d2..5a56b28b 100644
--- a/libsoup/soup-request-file.c
+++ b/libsoup/soup-request-file.c
@@ -25,15 +25,21 @@
#include <config.h>
#endif
-#include <glib/gi18n-lib.h>
-
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+#include <string.h>
#include "soup-request-file.h"
#include "soup.h"
#include "soup-directory-input-stream.h"
#include "soup-requester.h"
+/**
+ * SECTION:soup-request-file
+ * @short_description: SoupRequest support for "file" and "resource" URIs
+ *
+ * #SoupRequestFile implements #SoupRequest for "file" and "resource"
+ * URIs.
+ */
+
G_DEFINE_TYPE (SoupRequestFile, soup_request_file, SOUP_TYPE_REQUEST)
struct _SoupRequestFilePrivate {
@@ -128,7 +134,15 @@ soup_request_file_ensure_file (SoupRequestFile *file,
windowsify_file_uri_path (decoded_path);
#endif
- file->priv->gfile = g_file_new_for_path (decoded_path);
+ if (uri->scheme == SOUP_URI_SCHEME_RESOURCE) {
+ char *uri_str;
+
+ uri_str = g_strdup_printf ("resource://%s", decoded_path);
+ file->priv->gfile = g_file_new_for_uri (uri_str);
+ g_free (uri_str);
+ } else
+ file->priv->gfile = g_file_new_for_path (decoded_path);
+
g_free (decoded_path);
return TRUE;
}
@@ -184,36 +198,33 @@ soup_request_file_send (SoupRequest *request,
}
static void
-soup_request_file_send_async_thread (GSimpleAsyncResult *res,
- GObject *object,
- GCancellable *cancellable)
+soup_request_file_send_async_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
+ SoupRequest *request = source_object;
GInputStream *stream;
- SoupRequest *request;
GError *error = NULL;
- request = SOUP_REQUEST (object);
-
stream = soup_request_file_send (request, cancellable, &error);
-
if (stream == NULL)
- g_simple_async_result_take_error (res, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+ g_task_return_pointer (task, stream, g_object_unref);
}
static void
soup_request_file_send_async (SoupRequest *request,
GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *res;
-
- res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, soup_request_file_send_async);
+ GTask *task;
- g_simple_async_result_run_in_thread (res, soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable);
- g_object_unref (res);
+ task = g_task_new (request, cancellable, callback, user_data);
+ g_task_run_in_thread (task, soup_request_file_send_async_thread);
+ g_object_unref (task);
}
static GInputStream *
@@ -221,14 +232,9 @@ soup_request_file_send_finish (SoupRequest *request,
GAsyncResult *result,
GError **error)
{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
-
- g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == soup_request_file_send_async);
-
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
+ g_return_val_if_fail (g_task_is_valid (result, request), NULL);
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+ return g_task_propagate_pointer (G_TASK (result), error);
}
static goffset
@@ -250,7 +256,7 @@ soup_request_file_get_content_type (SoupRequest *request)
return file->priv->mime_type;
}
-static const char *file_schemes[] = { "file", NULL };
+static const char *file_schemes[] = { "file", "resource", NULL };
static void
soup_request_file_class_init (SoupRequestFileClass *request_file_class)
@@ -281,7 +287,7 @@ soup_request_file_class_init (SoupRequestFileClass *request_file_class)
*
* Return value: (transfer full): a #GFile corresponding to @file
*
- * Since: 2.34
+ * Since: 2.40
*/
GFile *
soup_request_file_get_file (SoupRequestFile *file)
diff --git a/libsoup/soup-request-file.h b/libsoup/soup-request-file.h
index acb1a085..df2cbab1 100644
--- a/libsoup/soup-request-file.h
+++ b/libsoup/soup-request-file.h
@@ -22,8 +22,6 @@
#ifndef SOUP_REQUEST_FILE_H
#define SOUP_REQUEST_FILE_H 1
-#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include "soup-request.h"
G_BEGIN_DECLS
@@ -47,12 +45,12 @@ typedef struct {
SoupRequestClass parent;
} SoupRequestFileClass;
+SOUP_AVAILABLE_IN_2_34
GType soup_request_file_get_type (void);
+SOUP_AVAILABLE_IN_2_34
GFile *soup_request_file_get_file (SoupRequestFile *file);
G_END_DECLS
-#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
-
#endif /* SOUP_REQUEST_FILE_H */
diff --git a/libsoup/soup-request-http.c b/libsoup/soup-request-http.c
index 44a1f7d5..9ae2c2a7 100644
--- a/libsoup/soup-request-http.c
+++ b/libsoup/soup-request-http.c
@@ -27,13 +27,23 @@
#include <glib/gi18n-lib.h>
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include "soup-request-http.h"
#include "soup.h"
-#include "soup-cache-private.h"
+#include "soup-message-private.h"
#include "soup-session-private.h"
+/**
+ * SECTION:soup-request-http
+ * @short_description: SoupRequest support for "http" and "https" URIs
+ *
+ * #SoupRequestHTTP implements #SoupRequest for "http" and "https"
+ * URIs.
+ *
+ * To do more complicated HTTP operations using the #SoupRequest APIs,
+ * call soup_request_http_get_message() to get the request's
+ * #SoupMessage.
+ */
+
G_DEFINE_TYPE (SoupRequestHTTP, soup_request_http, SOUP_TYPE_REQUEST)
struct _SoupRequestHTTPPrivate {
@@ -63,6 +73,8 @@ soup_request_http_check_uri (SoupRequest *request,
return FALSE;
http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ soup_message_set_soup_request (http->priv->msg, request);
+
g_signal_connect (http->priv->msg, "content-sniffed",
G_CALLBACK (content_sniffed), http);
return TRUE;
@@ -91,109 +103,28 @@ soup_request_http_send (SoupRequest *request,
GError **error)
{
SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
+ SoupSession *session = soup_request_get_session (request);
- return soup_session_send_request (soup_request_get_session (request),
- http->priv->msg,
- cancellable, error);
-}
-
-
-typedef struct {
- SoupRequestHTTP *http;
- GCancellable *cancellable;
- GSimpleAsyncResult *simple;
-
- SoupMessage *original;
- GInputStream *stream;
-} SendAsyncData;
-
-static void
-free_send_async_data (SendAsyncData *sadata)
-{
- g_object_unref (sadata->http);
- g_object_unref (sadata->simple);
-
- if (sadata->cancellable)
- g_object_unref (sadata->cancellable);
- if (sadata->stream)
- g_object_unref (sadata->stream);
- if (sadata->original)
- g_object_unref (sadata->original);
+ g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
- g_slice_free (SendAsyncData, sadata);
+ return soup_session_send (session, http->priv->msg,
+ cancellable, error);
}
+
static void
http_input_stream_ready_cb (GObject *source, GAsyncResult *result, gpointer user_data)
{
- SendAsyncData *sadata = user_data;
+ GTask *task = user_data;
GError *error = NULL;
GInputStream *stream;
- stream = soup_session_send_request_finish (SOUP_SESSION (source), result, &error);
- if (stream) {
- g_simple_async_result_set_op_res_gpointer (sadata->simple, stream, g_object_unref);
- } else {
- g_simple_async_result_take_error (sadata->simple, error);
- }
- g_simple_async_result_complete (sadata->simple);
- free_send_async_data (sadata);
-}
-
-
-static void
-conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
-{
- SendAsyncData *sadata = user_data;
- GInputStream *stream;
-
- if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
- SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
-
- stream = soup_cache_send_response (cache, sadata->original);
- if (stream) {
- g_simple_async_result_set_op_res_gpointer (sadata->simple, stream, g_object_unref);
-
- soup_message_got_headers (sadata->original);
-
- sadata->http->priv->content_type = g_strdup (soup_message_headers_get_content_type (msg->response_headers, NULL));
-
- g_simple_async_result_complete (sadata->simple);
-
- soup_message_finished (sadata->original);
- free_send_async_data (sadata);
- return;
- }
- }
-
- /* The resource was modified or the server returned a 200
- * OK. Either way we reload it. This is far from optimal as
- * we're donwloading the resource twice, but we will change it
- * once the cache is integrated in the streams stack.
- */
- soup_session_send_request_async (session, sadata->original, sadata->cancellable,
- http_input_stream_ready_cb, sadata);
-}
-
-static gboolean
-idle_return_from_cache_cb (gpointer data)
-{
- SendAsyncData *sadata = data;
-
- g_simple_async_result_set_op_res_gpointer (sadata->simple,
- g_object_ref (sadata->stream), g_object_unref);
-
- /* Issue signals */
- soup_message_got_headers (sadata->http->priv->msg);
-
- sadata->http->priv->content_type = g_strdup (soup_message_headers_get_content_type (sadata->http->priv->msg->response_headers, NULL));
-
- g_simple_async_result_complete (sadata->simple);
-
- soup_message_finished (sadata->http->priv->msg);
-
- free_send_async_data (sadata);
- return FALSE;
+ stream = soup_session_send_finish (SOUP_SESSION (source), result, &error);
+ if (stream)
+ g_task_return_pointer (task, stream, g_object_unref);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
static void
@@ -203,57 +134,14 @@ soup_request_http_send_async (SoupRequest *request,
gpointer user_data)
{
SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
- SendAsyncData *sadata;
- GInputStream *stream;
- SoupSession *session;
- SoupCache *cache;
-
- sadata = g_slice_new0 (SendAsyncData);
- sadata->http = g_object_ref (http);
- sadata->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- sadata->simple = g_simple_async_result_new (G_OBJECT (request), callback, user_data,
- soup_request_http_send_async);
-
- session = soup_request_get_session (request);
- cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
-
- if (cache) {
- SoupCacheResponse response;
-
- response = soup_cache_has_response (cache, http->priv->msg);
- if (response == SOUP_CACHE_RESPONSE_FRESH) {
- stream = soup_cache_send_response (cache, http->priv->msg);
-
- /* Cached resource file could have been deleted outside */
- if (stream) {
- /* Do return the stream asynchronously as in
- * the other cases. It's not enough to use
- * g_simple_async_result_complete_in_idle as
- * the signals must be also emitted
- * asynchronously
- */
- sadata->stream = stream;
- soup_add_completion (soup_session_get_async_context (session),
- idle_return_from_cache_cb, sadata);
- return;
- }
- } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
- SoupMessage *conditional_msg;
-
- conditional_msg = soup_cache_generate_conditional_request (cache, http->priv->msg);
-
- if (conditional_msg) {
- sadata->original = g_object_ref (http->priv->msg);
- soup_session_queue_message (session, conditional_msg,
- conditional_get_ready_cb,
- sadata);
- return;
- }
- }
- }
+ SoupSession *session = soup_request_get_session (request);
+ GTask *task;
+
+ g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
- soup_session_send_request_async (session, http->priv->msg, cancellable,
- http_input_stream_ready_cb, sadata);
+ task = g_task_new (request, cancellable, callback, user_data);
+ soup_session_send_async (session, http->priv->msg, cancellable,
+ http_input_stream_ready_cb, task);
}
static GInputStream *
@@ -261,14 +149,9 @@ soup_request_http_send_finish (SoupRequest *request,
GAsyncResult *result,
GError **error)
{
- GSimpleAsyncResult *simple;
-
- g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), soup_request_http_send_async), NULL);
+ g_return_val_if_fail (g_task_is_valid (result, request), NULL);
- simple = G_SIMPLE_ASYNC_RESULT (result);
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+ return g_task_propagate_pointer (G_TASK (result), error);
}
static goffset
@@ -342,7 +225,7 @@ soup_request_http_class_init (SoupRequestHTTPClass *request_http_class)
*
* Returns: (transfer full): a new reference to the #SoupMessage
*
- * Since: 2.34
+ * Since: 2.40
*/
SoupMessage *
soup_request_http_get_message (SoupRequestHTTP *http)
diff --git a/libsoup/soup-request-http.h b/libsoup/soup-request-http.h
index 6402646c..271a8052 100644
--- a/libsoup/soup-request-http.h
+++ b/libsoup/soup-request-http.h
@@ -22,8 +22,6 @@
#ifndef SOUP_REQUEST_HTTP_H
#define SOUP_REQUEST_HTTP_H 1
-#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include "soup-request.h"
G_BEGIN_DECLS
@@ -37,22 +35,22 @@ G_BEGIN_DECLS
typedef struct _SoupRequestHTTPPrivate SoupRequestHTTPPrivate;
-typedef struct {
+struct _SoupRequestHTTP {
SoupRequest parent;
SoupRequestHTTPPrivate *priv;
-} SoupRequestHTTP;
+};
typedef struct {
SoupRequestClass parent;
} SoupRequestHTTPClass;
+SOUP_AVAILABLE_IN_2_34
GType soup_request_http_get_type (void);
+SOUP_AVAILABLE_IN_2_34
SoupMessage *soup_request_http_get_message (SoupRequestHTTP *http);
G_END_DECLS
-#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
-
#endif /* SOUP_REQUEST_HTTP_H */
diff --git a/libsoup/soup-request.c b/libsoup/soup-request.c
index e7471c1c..0dfe3eff 100644
--- a/libsoup/soup-request.c
+++ b/libsoup/soup-request.c
@@ -27,8 +27,6 @@
#include <glib/gi18n-lib.h>
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include "soup-request.h"
#include "soup.h"
#include "soup-requester.h"
@@ -37,8 +35,8 @@
* SECTION:soup-request
* @short_description: Protocol-independent streaming request interface
*
- * A #SoupRequest is created by #SoupRequester, and represents a
- * request to retrieve a particular URI.
+ * A #SoupRequest is created by #SoupSession, and represents a request
+ * to retrieve a particular URI.
*/
/**
@@ -46,7 +44,7 @@
*
* A request to retrieve a particular URI.
*
- * Since: 2.34
+ * Since: 2.42
*/
static void soup_request_initable_interface_init (GInitableIface *initable_interface);
@@ -138,7 +136,7 @@ soup_request_initable_init (GInitable *initable,
gboolean ok;
if (!request->priv->uri) {
- g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_BAD_URI,
+ g_set_error (error, SOUP_REQUEST_ERROR, SOUP_REQUEST_ERROR_BAD_URI,
_("No URI provided"));
return FALSE;
}
@@ -148,7 +146,7 @@ soup_request_initable_init (GInitable *initable,
if (!ok && error && !*error) {
char *uri_string = soup_uri_to_string (request->priv->uri, FALSE);
- g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_BAD_URI,
+ g_set_error (error, SOUP_REQUEST_ERROR, SOUP_REQUEST_ERROR_BAD_URI,
_("Invalid '%s' URI: %s"),
request->priv->uri->scheme,
uri_string);
@@ -173,13 +171,18 @@ soup_request_default_send_async (SoupRequest *request,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *simple;
-
- simple = g_simple_async_result_new (G_OBJECT (request),
- callback, user_data,
- soup_request_default_send_async);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ GTask *task;
+ GInputStream *stream;
+ GError *error = NULL;
+
+ task = g_task_new (request, cancellable, callback, user_data);
+
+ stream = soup_request_send (request, cancellable, &error);
+ if (stream)
+ g_task_return_pointer (task, stream, g_object_unref);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
static GInputStream *
@@ -187,9 +190,7 @@ soup_request_default_send_finish (SoupRequest *request,
GAsyncResult *result,
GError **error)
{
- g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), soup_request_default_send_async), NULL);
-
- return soup_request_send (request, NULL, error);
+ return g_task_propagate_pointer (G_TASK (result), error);
}
/**
@@ -201,10 +202,13 @@ soup_request_default_send_finish (SoupRequest *request,
* Synchronously requests the URI pointed to by @request, and returns
* a #GInputStream that can be used to read its contents.
*
+ * Note that you cannot use this method with #SoupRequests attached to
+ * a #SoupSessionAsync.
+ *
* Return value: (transfer full): a #GInputStream that can be used to
* read from the URI pointed to by @request.
*
- * Since: 2.34
+ * Since: 2.42
*/
GInputStream *
soup_request_send (SoupRequest *request,
@@ -225,7 +229,10 @@ soup_request_send (SoupRequest *request,
* Begins an asynchronously request for the URI pointed to by
* @request.
*
- * Since: 2.34
+ * Note that you cannot use this method with #SoupRequests attached to
+ * a #SoupSessionSync.
+ *
+ * Since: 2.42
*/
void
soup_request_send_async (SoupRequest *request,
@@ -248,7 +255,7 @@ soup_request_send_async (SoupRequest *request,
* Return value: (transfer full): a #GInputStream that can be used to
* read from the URI pointed to by @request.
*
- * Since: 2.34
+ * Since: 2.42
*/
GInputStream *
soup_request_send_finish (SoupRequest *request,
@@ -274,6 +281,20 @@ soup_request_class_init (SoupRequestClass *request_class)
object_class->set_property = soup_request_set_property;
object_class->get_property = soup_request_get_property;
+ /**
+ * SOUP_REQUEST_URI:
+ *
+ * Alias for the #SoupRequest:uri property, qv.
+ *
+ * Since: 2.42
+ */
+ /**
+ * SoupRequest:uri:
+ *
+ * The request URI.
+ *
+ * Since: 2.42
+ */
g_object_class_install_property (
object_class, PROP_URI,
g_param_spec_boxed (SOUP_REQUEST_URI,
@@ -281,6 +302,20 @@ soup_request_class_init (SoupRequestClass *request_class)
"The request URI",
SOUP_TYPE_URI,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * SOUP_REQUEST_SESSION:
+ *
+ * Alias for the #SoupRequest:session property, qv.
+ *
+ * Since: 2.42
+ */
+ /**
+ * SoupRequest:session:
+ *
+ * The request's #SoupSession.
+ *
+ * Since: 2.42
+ */
g_object_class_install_property (
object_class, PROP_SESSION,
g_param_spec_object (SOUP_REQUEST_SESSION,
@@ -304,7 +339,7 @@ soup_request_initable_interface_init (GInitableIface *initable_interface)
*
* Return value: (transfer none): @request's URI
*
- * Since: 2.34
+ * Since: 2.42
*/
SoupURI *
soup_request_get_uri (SoupRequest *request)
@@ -320,7 +355,7 @@ soup_request_get_uri (SoupRequest *request)
*
* Return value: (transfer none): @request's #SoupSession
*
- * Since: 2.34
+ * Since: 2.42
*/
SoupSession *
soup_request_get_session (SoupRequest *request)
@@ -332,12 +367,14 @@ soup_request_get_session (SoupRequest *request)
* soup_request_get_content_length:
* @request: a #SoupRequest
*
- * Gets the length of the data represented by @request.
+ * Gets the length of the data represented by @request. For most
+ * request types, this will not be known until after you call
+ * soup_request_send() or soup_request_send_finish().
*
* Return value: the length of the data represented by @request,
* or -1 if not known.
*
- * Since: 2.34
+ * Since: 2.42
*/
goffset
soup_request_get_content_length (SoupRequest *request)
@@ -349,14 +386,17 @@ soup_request_get_content_length (SoupRequest *request)
* soup_request_get_content_type:
* @request: a #SoupRequest
*
- * Gets the type of the data represented by @request. As in the
- * HTTP Content-Type header, this may include parameters after
- * the MIME type.
+ * Gets the type of the data represented by @request. For most request
+ * types, this will not be known until after you call
+ * soup_request_send() or soup_request_send_finish().
+ *
+ * As in the HTTP Content-Type header, this may include parameters
+ * after the MIME type.
*
* Return value: the type of the data represented by @request,
* or %NULL if not known.
*
- * Since: 2.34
+ * Since: 2.42
*/
const char *
soup_request_get_content_type (SoupRequest *request)
diff --git a/libsoup/soup-request.h b/libsoup/soup-request.h
index a48e9174..84ad6d6b 100644
--- a/libsoup/soup-request.h
+++ b/libsoup/soup-request.h
@@ -22,8 +22,6 @@
#ifndef SOUP_REQUEST_H
#define SOUP_REQUEST_H 1
-#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
-
#include <gio/gio.h>
#include <libsoup/soup-types.h>
@@ -37,7 +35,6 @@ G_BEGIN_DECLS
#define SOUP_IS_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_REQUEST))
#define SOUP_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_REQUEST, SoupRequestClass))
-typedef struct _SoupRequest SoupRequest;
typedef struct _SoupRequestPrivate SoupRequestPrivate;
typedef struct _SoupRequestClass SoupRequestClass;
@@ -71,30 +68,36 @@ struct _SoupRequestClass {
const char * (*get_content_type) (SoupRequest *request);
};
+SOUP_AVAILABLE_IN_2_34
GType soup_request_get_type (void);
#define SOUP_REQUEST_URI "uri"
#define SOUP_REQUEST_SESSION "session"
+SOUP_AVAILABLE_IN_2_34
GInputStream *soup_request_send (SoupRequest *request,
GCancellable *cancellable,
GError **error);
+SOUP_AVAILABLE_IN_2_34
void soup_request_send_async (SoupRequest *request,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
+SOUP_AVAILABLE_IN_2_34
GInputStream *soup_request_send_finish (SoupRequest *request,
GAsyncResult *result,
GError **error);
+SOUP_AVAILABLE_IN_2_34
SoupURI *soup_request_get_uri (SoupRequest *request);
+SOUP_AVAILABLE_IN_2_34
SoupSession *soup_request_get_session (SoupRequest *request);
+SOUP_AVAILABLE_IN_2_34
goffset soup_request_get_content_length (SoupRequest *request);
+SOUP_AVAILABLE_IN_2_34
const char *soup_request_get_content_type (SoupRequest *request);
G_END_DECLS
-#endif /* LIBSOUP_USE_UNSTABLE_REQUEST_API */
-
#endif /* SOUP_REQUEST_H */
diff --git a/libsoup/soup-requester.c b/libsoup/soup-requester.c
index 281ebb17..c67bfe31 100644
--- a/libsoup/soup-requester.c
+++ b/libsoup/soup-requester.c
@@ -22,22 +22,18 @@
#include "config.h"
-#include <glib/gi18n-lib.h>
-
#define LIBSOUP_USE_UNSTABLE_REQUEST_API
#include "soup-requester.h"
#include "soup.h"
-#include "soup-request-data.h"
-#include "soup-request-file.h"
-#include "soup-request-http.h"
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static SoupSessionFeatureInterface *soup_requester_default_feature_interface;
static void soup_requester_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
struct _SoupRequesterPrivate {
SoupSession *session;
- GHashTable *request_types;
};
G_DEFINE_TYPE_WITH_CODE (SoupRequester, soup_requester, G_TYPE_OBJECT,
@@ -47,44 +43,19 @@ G_DEFINE_TYPE_WITH_CODE (SoupRequester, soup_requester, G_TYPE_OBJECT,
static void
soup_requester_init (SoupRequester *requester)
{
- SoupSessionFeature *feature;
-
requester->priv = G_TYPE_INSTANCE_GET_PRIVATE (requester,
SOUP_TYPE_REQUESTER,
SoupRequesterPrivate);
-
- requester->priv->request_types = g_hash_table_new (soup_str_case_hash,
- soup_str_case_equal);
-
- feature = SOUP_SESSION_FEATURE (requester);
- soup_session_feature_add_feature (feature, SOUP_TYPE_REQUEST_HTTP);
- soup_session_feature_add_feature (feature, SOUP_TYPE_REQUEST_FILE);
- soup_session_feature_add_feature (feature, SOUP_TYPE_REQUEST_DATA);
-}
-
-static void
-soup_requester_finalize (GObject *object)
-{
- SoupRequester *requester = SOUP_REQUESTER (object);
-
- g_hash_table_destroy (requester->priv->request_types);
-
- G_OBJECT_CLASS (soup_requester_parent_class)->finalize (object);
}
static void
soup_requester_class_init (SoupRequesterClass *requester_class)
{
- GObjectClass *object_class = G_OBJECT_CLASS (requester_class);
-
g_type_class_add_private (requester_class, sizeof (SoupRequesterPrivate));
-
- /* virtual method override */
- object_class->finalize = soup_requester_finalize;
}
static void
-attach (SoupSessionFeature *feature, SoupSession *session)
+soup_requester_attach (SoupSessionFeature *feature, SoupSession *session)
{
SoupRequester *requester = SOUP_REQUESTER (feature);
@@ -94,7 +65,7 @@ attach (SoupSessionFeature *feature, SoupSession *session)
}
static void
-detach (SoupSessionFeature *feature, SoupSession *session)
+soup_requester_detach (SoupSessionFeature *feature, SoupSession *session)
{
SoupRequester *requester = SOUP_REQUESTER (feature);
@@ -104,63 +75,38 @@ detach (SoupSessionFeature *feature, SoupSession *session)
}
static gboolean
-add_feature (SoupSessionFeature *feature, GType type)
+soup_requester_add_feature (SoupSessionFeature *feature, GType type)
{
SoupRequester *requester = SOUP_REQUESTER (feature);
- SoupRequestClass *request_class;
- int i;
if (!g_type_is_a (type, SOUP_TYPE_REQUEST))
return FALSE;
- request_class = g_type_class_ref (type);
- for (i = 0; request_class->schemes[i]; i++) {
- g_hash_table_insert (requester->priv->request_types,
- (char *)request_class->schemes[i],
- GSIZE_TO_POINTER (type));
- }
+ soup_session_add_feature_by_type (requester->priv->session, type);
return TRUE;
}
static gboolean
-remove_feature (SoupSessionFeature *feature, GType type)
+soup_requester_remove_feature (SoupSessionFeature *feature, GType type)
{
SoupRequester *requester = SOUP_REQUESTER (feature);
- SoupRequestClass *request_class;
- int i, orig_size;
if (!g_type_is_a (type, SOUP_TYPE_REQUEST))
return FALSE;
- request_class = g_type_class_peek (type);
- if (!request_class)
- return FALSE;
-
- orig_size = g_hash_table_size (requester->priv->request_types);
- for (i = 0; request_class->schemes[i]; i++) {
- g_hash_table_remove (requester->priv->request_types,
- request_class->schemes[i]);
- }
-
- return g_hash_table_size (requester->priv->request_types) != orig_size;
+ soup_session_remove_feature_by_type (requester->priv->session, type);
+ return TRUE;
}
static gboolean
-has_feature (SoupSessionFeature *feature, GType type)
+soup_requester_has_feature (SoupSessionFeature *feature, GType type)
{
SoupRequester *requester = SOUP_REQUESTER (feature);
- GHashTableIter iter;
- gpointer key, value;
if (!g_type_is_a (type, SOUP_TYPE_REQUEST))
return FALSE;
- g_hash_table_iter_init (&iter, requester->priv->request_types);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- if (value == GSIZE_TO_POINTER (type))
- return TRUE;
- }
- return FALSE;
+ return soup_session_has_feature (requester->priv->session, type);
}
static void
@@ -170,114 +116,77 @@ soup_requester_session_feature_init (SoupSessionFeatureInterface *feature_interf
soup_requester_default_feature_interface =
g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
- feature_interface->attach = attach;
- feature_interface->detach = detach;
- feature_interface->add_feature = add_feature;
- feature_interface->remove_feature = remove_feature;
- feature_interface->has_feature = has_feature;
+ feature_interface->attach = soup_requester_attach;
+ feature_interface->detach = soup_requester_detach;
+ feature_interface->add_feature = soup_requester_add_feature;
+ feature_interface->remove_feature = soup_requester_remove_feature;
+ feature_interface->has_feature = soup_requester_has_feature;
}
-/**
- * soup_requester_new:
- *
- * Creates a new #SoupRequester object, which can be added to
- * a #SoupSession with soup_session_add_feature().
- *
- * Return value: the new #SoupRequester
- *
- * Since: 2.34
- */
SoupRequester *
soup_requester_new (void)
{
return g_object_new (SOUP_TYPE_REQUESTER, NULL);
}
+static void
+translate_error (GError *error)
+{
+ if (error->domain != SOUP_REQUEST_ERROR)
+ return;
+
+ error->domain = SOUP_REQUESTER_ERROR;
+ if (error->code == SOUP_REQUEST_ERROR_BAD_URI)
+ error->code = SOUP_REQUESTER_ERROR_BAD_URI;
+ else if (error->code == SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME)
+ error->code = SOUP_REQUESTER_ERROR_UNSUPPORTED_URI_SCHEME;
+ else
+ g_warn_if_reached ();
+}
+
/**
* soup_requester_request:
- * @requester: a #SoupRequester
- * @uri_string: a URI, in string form
- * @error: return location for a #GError, or %NULL
- *
- * Creates a #SoupRequest for retrieving @uri_string.
- *
- * Return value: (transfer full): a new #SoupRequest, or
- * %NULL on error.
*
- * Since: 2.34
+ * Return value: (transfer full):
*/
SoupRequest *
soup_requester_request (SoupRequester *requester, const char *uri_string,
GError **error)
{
- SoupURI *uri;
SoupRequest *req;
- uri = soup_uri_new (uri_string);
- if (!uri) {
- g_set_error (error, SOUP_REQUESTER_ERROR, SOUP_REQUESTER_ERROR_BAD_URI,
- _("Could not parse URI '%s'"), uri_string);
- return NULL;
- }
+ g_return_val_if_fail (SOUP_IS_REQUESTER (requester), NULL);
+
+ req = soup_session_request (requester->priv->session,
+ uri_string, error);
+ if (req || !error)
+ return req;
- req = soup_requester_request_uri (requester, uri, error);
- soup_uri_free (uri);
- return req;
+ translate_error (*error);
+ return NULL;
}
/**
* soup_requester_request_uri:
- * @requester: a #SoupRequester
- * @uri: a #SoupURI representing the URI to retrieve
- * @error: return location for a #GError, or %NULL
- *
- * Creates a #SoupRequest for retrieving @uri.
*
- * Return value: (transfer full): a new #SoupRequest, or
- * %NULL on error.
- *
- * Since: 2.34
+ * Return value: (transfer full):
*/
SoupRequest *
soup_requester_request_uri (SoupRequester *requester, SoupURI *uri,
GError **error)
{
- GType request_type;
+ SoupRequest *req;
g_return_val_if_fail (SOUP_IS_REQUESTER (requester), NULL);
- request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (requester->priv->request_types, uri->scheme));
- if (!request_type) {
- g_set_error (error, SOUP_REQUESTER_ERROR,
- SOUP_REQUESTER_ERROR_UNSUPPORTED_URI_SCHEME,
- _("Unsupported URI scheme '%s'"), uri->scheme);
- return NULL;
- }
-
- return g_initable_new (request_type, NULL, error,
- "uri", uri,
- "session", requester->priv->session,
- NULL);
-}
+ req = soup_session_request_uri (requester->priv->session,
+ uri, error);
+ if (req || !error)
+ return req;
-/**
- * SOUP_REQUESTER_ERROR:
- *
- * A #GError domain for #SoupRequester errors. Used with
- * #SoupRequesterError.
- *
- * Since: 2.34
- */
-/**
- * SoupRequesterError:
- * @SOUP_REQUESTER_ERROR_BAD_URI: the URI could not be parsed
- * @SOUP_REQUESTER_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
- * supported by this #SoupRequester
- *
- * A #SoupRequester error.
- *
- * Since: 2.34
- */
+ translate_error (*error);
+ return NULL;
+}
GQuark
soup_requester_error_quark (void)
@@ -287,3 +196,5 @@ soup_requester_error_quark (void)
error = g_quark_from_static_string ("soup_requester_error_quark");
return error;
}
+
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/libsoup/soup-requester.h b/libsoup/soup-requester.h
index a10f1e55..06355131 100644
--- a/libsoup/soup-requester.h
+++ b/libsoup/soup-requester.h
@@ -24,7 +24,6 @@
#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
#include <libsoup/soup-types.h>
-#include <libsoup/soup-request.h>
G_BEGIN_DECLS
@@ -48,18 +47,28 @@ typedef struct {
GObjectClass parent_class;
} SoupRequesterClass;
+SOUP_AVAILABLE_IN_2_34
+SOUP_DEPRECATED_IN_2_42
GType soup_requester_get_type (void);
+SOUP_AVAILABLE_IN_2_34
+SOUP_DEPRECATED_IN_2_42
SoupRequester *soup_requester_new (void);
+SOUP_AVAILABLE_IN_2_34
+SOUP_DEPRECATED_IN_2_42_FOR(soup_session_request)
SoupRequest *soup_requester_request (SoupRequester *requester,
const char *uri_string,
GError **error);
+SOUP_AVAILABLE_IN_2_34
+SOUP_DEPRECATED_IN_2_42_FOR(soup_session_request_uri)
SoupRequest *soup_requester_request_uri (SoupRequester *requester,
SoupURI *uri,
GError **error);
+SOUP_AVAILABLE_IN_2_34
+SOUP_DEPRECATED_IN_2_42_FOR(SOUP_REQUEST_ERROR)
GQuark soup_requester_error_quark (void);
#define SOUP_REQUESTER_ERROR soup_requester_error_quark ()
diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c
index 1e462ea2..07d801d1 100644
--- a/libsoup/soup-server.c
+++ b/libsoup/soup-server.c
@@ -14,7 +14,7 @@
#include "soup-server.h"
#include "soup.h"
#include "soup-message-private.h"
-#include "soup-marshal.h"
+#include "soup-misc-private.h"
#include "soup-path-map.h"
/**
@@ -106,6 +106,8 @@ typedef struct {
GSList *auth_domains;
GMainContext *async_context;
+
+ char **http_aliases, **https_aliases;
} SoupServerPrivate;
#define SOUP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SERVER, SoupServerPrivate))
@@ -122,6 +124,8 @@ enum {
PROP_ASYNC_CONTEXT,
PROP_RAW_PATHS,
PROP_SERVER_HEADER,
+ PROP_HTTP_ALIASES,
+ PROP_HTTPS_ALIASES,
LAST_PROP
};
@@ -142,6 +146,10 @@ soup_server_init (SoupServer *server)
SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
priv->handlers = soup_path_map_new ((GDestroyNotify)free_handler);
+
+ priv->http_aliases = g_new (char *, 2);
+ priv->http_aliases[0] = (char *)g_intern_string ("*");
+ priv->http_aliases[1] = NULL;
}
static void
@@ -192,6 +200,9 @@ soup_server_finalize (GObject *object)
g_clear_pointer (&priv->loop, g_main_loop_unref);
g_clear_pointer (&priv->async_context, g_main_context_unref);
+ g_free (priv->http_aliases);
+ g_free (priv->https_aliases);
+
G_OBJECT_CLASS (soup_server_parent_class)->finalize (object);
}
@@ -251,6 +262,29 @@ soup_server_constructor (GType type,
return server;
}
+/* priv->http_aliases and priv->https_aliases are stored as arrays of
+ * *interned* strings, so we can't just use g_strdupv() to set them.
+ */
+static void
+set_aliases (char ***variable, char **value)
+{
+ int len, i;
+
+ if (*variable)
+ g_free (*variable);
+
+ if (!value) {
+ *variable = NULL;
+ return;
+ }
+
+ len = g_strv_length (value);
+ *variable = g_new (char *, len + 1);
+ for (i = 0; i < len; i++)
+ (*variable)[i] = (char *)g_intern_string (value[i]);
+ (*variable)[i] = NULL;
+}
+
static void
soup_server_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
@@ -305,6 +339,12 @@ soup_server_set_property (GObject *object, guint prop_id,
} else
priv->server_header = g_strdup (header);
break;
+ case PROP_HTTP_ALIASES:
+ set_aliases (&priv->http_aliases, g_value_get_boxed (value));
+ break;
+ case PROP_HTTPS_ALIASES:
+ set_aliases (&priv->https_aliases, g_value_get_boxed (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -342,6 +382,12 @@ soup_server_get_property (GObject *object, guint prop_id,
case PROP_SERVER_HEADER:
g_value_set_string (value, priv->server_header);
break;
+ case PROP_HTTP_ALIASES:
+ g_value_set_boxed (value, priv->http_aliases);
+ break;
+ case PROP_HTTPS_ALIASES:
+ g_value_set_boxed (value, priv->https_aliases);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -387,7 +433,7 @@ soup_server_class_init (SoupServerClass *server_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupServerClass, request_started),
NULL, NULL,
- _soup_marshal_NONE__OBJECT_POINTER,
+ NULL,
G_TYPE_NONE, 2,
SOUP_TYPE_MESSAGE,
SOUP_TYPE_CLIENT_CONTEXT);
@@ -412,7 +458,7 @@ soup_server_class_init (SoupServerClass *server_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupServerClass, request_read),
NULL, NULL,
- _soup_marshal_NONE__OBJECT_POINTER,
+ NULL,
G_TYPE_NONE, 2,
SOUP_TYPE_MESSAGE,
SOUP_TYPE_CLIENT_CONTEXT);
@@ -432,7 +478,7 @@ soup_server_class_init (SoupServerClass *server_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupServerClass, request_finished),
NULL, NULL,
- _soup_marshal_NONE__OBJECT_POINTER,
+ NULL,
G_TYPE_NONE, 2,
SOUP_TYPE_MESSAGE,
SOUP_TYPE_CLIENT_CONTEXT);
@@ -461,7 +507,7 @@ soup_server_class_init (SoupServerClass *server_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupServerClass, request_aborted),
NULL, NULL,
- _soup_marshal_NONE__OBJECT_POINTER,
+ NULL,
G_TYPE_NONE, 2,
SOUP_TYPE_MESSAGE,
SOUP_TYPE_CLIENT_CONTEXT);
@@ -629,6 +675,68 @@ soup_server_class_init (SoupServerClass *server_class)
"Server header",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * SoupServer:http-aliases:
+ *
+ * A %NULL-terminated array of URI schemes that should be
+ * considered to be aliases for "http". Eg, if this included
+ * <literal>"dav"</literal>, than a URI of
+ * <literal>dav://example.com/path</literal> would be treated
+ * identically to <literal>http://example.com/path</literal>.
+ * In particular, this is needed in cases where a client
+ * sends requests with absolute URIs, where those URIs do
+ * not use "http:".
+ *
+ * The default value is an array containing the single element
+ * <literal>"*"</literal>, a special value which means that
+ * any scheme except "https" is considered to be an alias for
+ * "http".
+ *
+ * See also #SoupServer:https-aliases.
+ *
+ * Since: 2.44
+ */
+ /**
+ * SOUP_SERVERI_HTTP_ALIASES:
+ *
+ * Alias for the #SoupServer:http-aliases property, qv.
+ *
+ * Since: 2.44
+ */
+ g_object_class_install_property (
+ object_class, PROP_HTTP_ALIASES,
+ g_param_spec_boxed (SOUP_SERVER_HTTP_ALIASES,
+ "http aliases",
+ "URI schemes that are considered aliases for 'http'",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE));
+ /**
+ * SoupServer:https-aliases:
+ *
+ * A comma-delimited list of URI schemes that should be
+ * considered to be aliases for "https". See
+ * #SoupServer:http-aliases for more information.
+ *
+ * The default value is %NULL, meaning that no URI schemes
+ * are considered aliases for "https".
+ *
+ * Since: 2.44
+ */
+ /**
+ * SOUP_SERVER_HTTPS_ALIASES:
+ *
+ * Alias for the #SoupServer:https-aliases property, qv.
+ *
+ * Since: 2.44
+ **/
+ g_object_class_install_property (
+ object_class, PROP_HTTPS_ALIASES,
+ g_param_spec_boxed (SOUP_SERVER_HTTPS_ALIASES,
+ "https aliases",
+ "URI schemes that are considered aliases for 'https'",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE));
}
/**
@@ -680,7 +788,8 @@ soup_server_get_port (SoupServer *server)
*
* In order for a server to run https, you must set the
* %SOUP_SERVER_SSL_CERT_FILE and %SOUP_SERVER_SSL_KEY_FILE properties
- * to provide it with an SSL certificate to use.
+ * or %SOUP_SERVER_TLS_CERTIFICATE property to provide it with an SSL
+ * certificate to use.
*
* Return value: %TRUE if @server is serving https.
**/
@@ -692,7 +801,7 @@ soup_server_is_https (SoupServer *server)
g_return_val_if_fail (SOUP_IS_SERVER (server), 0);
priv = SOUP_SERVER_GET_PRIVATE (server);
- return (priv->ssl_cert_file && priv->ssl_key_file);
+ return priv->ssl_cert != NULL;
}
/**
@@ -805,7 +914,7 @@ soup_server_get_handler (SoupServer *server, const char *path)
}
static void
-got_headers (SoupMessage *req, SoupClientContext *client)
+got_headers (SoupMessage *msg, SoupClientContext *client)
{
SoupServer *server = client->server;
SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
@@ -817,17 +926,23 @@ got_headers (SoupMessage *req, SoupClientContext *client)
gboolean rejected = FALSE;
char *auth_user;
+ uri = soup_message_get_uri (msg);
+ if ((soup_server_is_https (server) && !soup_uri_is_https (uri, priv->https_aliases)) ||
+ (!soup_server_is_https (server) && !soup_uri_is_http (uri, priv->http_aliases))) {
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ return;
+ }
+
if (!priv->raw_paths) {
char *decoded_path;
- uri = soup_message_get_uri (req);
decoded_path = soup_uri_decode (uri->path);
if (strstr (decoded_path, "/../") ||
g_str_has_suffix (decoded_path, "/..")) {
/* Introducing new ".." segments is not allowed */
g_free (decoded_path);
- soup_message_set_status (req, SOUP_STATUS_BAD_REQUEST);
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
return;
}
@@ -838,7 +953,7 @@ got_headers (SoupMessage *req, SoupClientContext *client)
/* Add required response headers */
date = soup_date_new_from_now (0);
date_string = soup_date_to_string (date, SOUP_DATE_HTTP);
- soup_message_headers_replace (req->response_headers, "Date",
+ soup_message_headers_replace (msg->response_headers, "Date",
date_string);
g_free (date_string);
soup_date_free (date);
@@ -851,8 +966,8 @@ got_headers (SoupMessage *req, SoupClientContext *client)
for (iter = priv->auth_domains; iter; iter = iter->next) {
domain = iter->data;
- if (soup_auth_domain_covers (domain, req)) {
- auth_user = soup_auth_domain_accepts (domain, req);
+ if (soup_auth_domain_covers (domain, msg)) {
+ auth_user = soup_auth_domain_accepts (domain, msg);
if (auth_user) {
client->auth_domain = g_object_ref (domain);
client->auth_user = auth_user;
@@ -870,27 +985,27 @@ got_headers (SoupMessage *req, SoupClientContext *client)
for (iter = priv->auth_domains; iter; iter = iter->next) {
domain = iter->data;
- if (soup_auth_domain_covers (domain, req))
- soup_auth_domain_challenge (domain, req);
+ if (soup_auth_domain_covers (domain, msg))
+ soup_auth_domain_challenge (domain, msg);
}
}
static void
-call_handler (SoupMessage *req, SoupClientContext *client)
+call_handler (SoupMessage *msg, SoupClientContext *client)
{
SoupServer *server = client->server;
SoupServerHandler *hand;
SoupURI *uri;
- g_signal_emit (server, signals[REQUEST_READ], 0, req, client);
+ g_signal_emit (server, signals[REQUEST_READ], 0, msg, client);
- if (req->status_code != 0)
+ if (msg->status_code != 0)
return;
- uri = soup_message_get_uri (req);
+ uri = soup_message_get_uri (msg);
hand = soup_server_get_handler (server, uri->path);
if (!hand) {
- soup_message_set_status (req, SOUP_STATUS_NOT_FOUND);
+ soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
return;
}
@@ -903,12 +1018,12 @@ call_handler (SoupMessage *req, SoupClientContext *client)
form_data_set = NULL;
/* Call method handler */
- (*hand->callback) (server, req,
+ (*hand->callback) (server, msg,
uri->path, form_data_set,
client, hand->user_data);
if (form_data_set)
- g_hash_table_destroy (form_data_set);
+ g_hash_table_unref (form_data_set);
}
}
@@ -1436,6 +1551,10 @@ soup_server_remove_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
* Pauses I/O on @msg. This can be used when you need to return from
* the server handler without having the full response ready yet. Use
* soup_server_unpause_message() to resume I/O.
+ *
+ * This must only be called on #SoupMessages which were created by the
+ * #SoupServer and are currently doing I/O, such as those passed into a
+ * #SoupServerCallback or emitted in a #SoupServer::request-read signal.
**/
void
soup_server_pause_message (SoupServer *server,
@@ -1457,6 +1576,10 @@ soup_server_pause_message (SoupServer *server,
* chunked response.
*
* I/O won't actually resume until you return to the main loop.
+ *
+ * This must only be called on #SoupMessages which were created by the
+ * #SoupServer and are currently doing I/O, such as those passed into a
+ * #SoupServerCallback or emitted in a #SoupServer::request-read signal.
**/
void
soup_server_unpause_message (SoupServer *server,
diff --git a/libsoup/soup-server.h b/libsoup/soup-server.h
index e1c9bbfb..0d09322e 100644
--- a/libsoup/soup-server.h
+++ b/libsoup/soup-server.h
@@ -64,6 +64,8 @@ typedef void (*SoupServerCallback) (SoupServer *server,
#define SOUP_SERVER_ASYNC_CONTEXT "async-context"
#define SOUP_SERVER_RAW_PATHS "raw-paths"
#define SOUP_SERVER_SERVER_HEADER "server-header"
+#define SOUP_SERVER_HTTP_ALIASES "http-aliases"
+#define SOUP_SERVER_HTTPS_ALIASES "https-aliases"
SoupServer *soup_server_new (const char *optname1,
...) G_GNUC_NULL_TERMINATED;
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index 46900552..b9353480 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -9,64 +9,44 @@
#include <config.h>
#endif
-#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-
#include "soup-session-async.h"
#include "soup.h"
#include "soup-session-private.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
+#include "soup-misc-private.h"
/**
* SECTION:soup-session-async
- * @short_description: Soup session for asynchronous (main-loop-based) I/O.
+ * @short_description: (Deprecated) SoupSession for asynchronous
+ * (main-loop-based) I/O.
*
* #SoupSessionAsync is an implementation of #SoupSession that uses
- * non-blocking I/O via the glib main loop. It is intended for use in
- * single-threaded programs.
+ * non-blocking I/O via the glib main loop for all I/O.
+ *
+ * As of libsoup 2.42, this is deprecated in favor of the plain
+ * #SoupSession class (which uses both asynchronous and synchronous
+ * I/O, depending on the API used). See the <link
+ * linkend="libsoup-session-porting">porting guide</link>.
**/
-static void run_queue (SoupSessionAsync *sa);
-static void do_idle_run_queue (SoupSession *session);
-
-static void send_request_running (SoupSession *session, SoupMessageQueueItem *item);
-static void send_request_restarted (SoupSession *session, SoupMessageQueueItem *item);
-static void send_request_finished (SoupSession *session, SoupMessageQueueItem *item);
-
G_DEFINE_TYPE (SoupSessionAsync, soup_session_async, SOUP_TYPE_SESSION)
-typedef struct {
- SoupSessionAsync *sa;
- gboolean disposed;
-
-} SoupSessionAsyncPrivate;
-#define SOUP_SESSION_ASYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncPrivate))
-
static void
soup_session_async_init (SoupSessionAsync *sa)
{
- SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (sa);
-
- priv->sa = sa;
-}
-
-static void
-soup_session_async_dispose (GObject *object)
-{
- SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (object);
-
- priv->disposed = TRUE;
-
- G_OBJECT_CLASS (soup_session_async_parent_class)->dispose (object);
}
-
/**
* soup_session_async_new:
*
* Creates an asynchronous #SoupSession with the default options.
*
* Return value: the new session.
+ *
+ * Deprecated: #SoupSessionAsync is deprecated; use a plain
+ * #SoupSession, created with soup_session_new(). See the <link
+ * linkend="libsoup-session-porting">porting guide</link>.
**/
SoupSession *
soup_session_async_new (void)
@@ -82,6 +62,10 @@ soup_session_async_new (void)
* Creates an asynchronous #SoupSession with the specified options.
*
* Return value: the new session.
+ *
+ * Deprecated: #SoupSessionAsync is deprecated; use a plain
+ * #SoupSession, created with soup_session_new_with_options(). See the
+ * <link linkend="libsoup-session-porting">porting guide</link>.
**/
SoupSession *
soup_session_async_new_with_options (const char *optname1, ...)
@@ -97,331 +81,23 @@ soup_session_async_new_with_options (const char *optname1, ...)
return session;
}
-static void
-connection_closed (SoupConnection *conn, gpointer session)
-{
- /* Run the queue in case anyone was waiting for a connection
- * to be closed.
- */
- do_idle_run_queue (session);
-}
-
-static void
-message_completed (SoupMessage *msg, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- do_idle_run_queue (item->session);
-
- if (item->state != SOUP_MESSAGE_RESTARTING)
- item->state = SOUP_MESSAGE_FINISHING;
-}
-
-static void
-tunnel_complete (SoupMessageQueueItem *item)
-{
- SoupSession *session = item->session;
-
- soup_message_finished (item->msg);
- if (item->related->msg->status_code)
- item->related->state = SOUP_MESSAGE_FINISHING;
- else
- soup_message_set_https_status (item->related->msg, item->conn);
-
- do_idle_run_queue (session);
- soup_message_queue_item_unref (item->related);
- soup_session_unqueue_item (session, item);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
-}
-
-static void
-ssl_tunnel_completed (SoupConnection *conn, guint status, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- g_signal_connect (item->conn, "disconnected",
- G_CALLBACK (connection_closed), item->session);
- soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
- soup_connection_set_state (item->conn, SOUP_CONNECTION_IN_USE);
-
- item->related->state = SOUP_MESSAGE_READY;
- } else {
- if (item->conn)
- soup_connection_disconnect (item->conn);
- soup_message_set_status (item->related->msg, SOUP_STATUS_SSL_FAILED);
- }
-
- tunnel_complete (item);
-}
-
-static void
-tunnel_message_completed (SoupMessage *msg, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
- SoupSession *session = item->session;
-
- if (item->state == SOUP_MESSAGE_RESTARTING) {
- soup_message_restarted (msg);
- if (item->conn) {
- item->state = SOUP_MESSAGE_RUNNING;
- soup_session_send_queue_item (session, item, tunnel_message_completed);
- return;
- }
-
- soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
- }
-
- item->state = SOUP_MESSAGE_FINISHED;
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- if (item->conn)
- soup_connection_disconnect (item->conn);
- if (msg->status_code == SOUP_STATUS_TRY_AGAIN) {
- item->related->state = SOUP_MESSAGE_AWAITING_CONNECTION;
- soup_message_queue_item_set_connection (item->related, NULL);
- } else
- soup_message_set_status (item->related->msg, msg->status_code);
-
- tunnel_complete (item);
- return;
- }
-
- soup_connection_start_ssl_async (item->conn, item->cancellable,
- ssl_tunnel_completed, item);
-}
-
-static void
-got_connection (SoupConnection *conn, guint status, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
- SoupSession *session = item->session;
-
- if (item->state != SOUP_MESSAGE_CONNECTING) {
- soup_connection_disconnect (conn);
- do_idle_run_queue (session);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
- return;
- }
-
- soup_message_set_https_status (item->msg, conn);
-
- if (status != SOUP_STATUS_OK) {
- soup_connection_disconnect (conn);
-
- if (status == SOUP_STATUS_TRY_AGAIN) {
- soup_message_queue_item_set_connection (item, NULL);
- item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
- } else {
- soup_session_set_item_status (session, item, status);
- item->state = SOUP_MESSAGE_FINISHING;
- }
-
- do_idle_run_queue (session);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
- return;
- }
-
- if (soup_connection_is_tunnelled (conn)) {
- SoupMessageQueueItem *tunnel_item;
-
- item->state = SOUP_MESSAGE_TUNNELING;
-
- tunnel_item = soup_session_make_connect_message (session, conn);
- tunnel_item->related = item;
- soup_session_send_queue_item (session, tunnel_item, tunnel_message_completed);
- return;
- }
-
- item->state = SOUP_MESSAGE_READY;
- g_signal_connect (conn, "disconnected",
- G_CALLBACK (connection_closed), session);
- run_queue ((SoupSessionAsync *)session);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
-}
-
-static void
-process_queue_item (SoupMessageQueueItem *item,
- gboolean *should_prune,
- gboolean loop)
-{
- SoupSession *session = item->session;
-
- if (item->async_context != soup_session_get_async_context (session))
- return;
-
- do {
- if (item->paused)
- return;
-
- switch (item->state) {
- case SOUP_MESSAGE_STARTING:
- item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
- break;
-
- case SOUP_MESSAGE_AWAITING_CONNECTION:
- if (!soup_session_get_connection (session, item, should_prune))
- return;
-
- if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
- item->state = SOUP_MESSAGE_READY;
- break;
- }
-
- item->state = SOUP_MESSAGE_CONNECTING;
- soup_message_queue_item_ref (item);
- g_object_ref (session);
- soup_connection_connect_async (item->conn, item->cancellable,
- got_connection, item);
- return;
-
- case SOUP_MESSAGE_READY:
- item->state = SOUP_MESSAGE_RUNNING;
- soup_session_send_queue_item (session, item, message_completed);
- if (item->new_api)
- send_request_running (session, item);
- break;
-
- case SOUP_MESSAGE_RESTARTING:
- item->state = SOUP_MESSAGE_STARTING;
- soup_message_restarted (item->msg);
- if (item->new_api)
- send_request_restarted (session, item);
- break;
-
- case SOUP_MESSAGE_FINISHING:
- item->state = SOUP_MESSAGE_FINISHED;
- soup_message_finished (item->msg);
- if (item->state != SOUP_MESSAGE_FINISHED)
- break;
-
- g_object_ref (session);
- soup_session_unqueue_item (session, item);
- if (item->callback)
- item->callback (session, item->msg, item->callback_data);
- else if (item->new_api)
- send_request_finished (session, item);
- do_idle_run_queue (session);
- g_object_unref (session);
- return;
-
- default:
- /* Nothing to do with this message in any
- * other state.
- */
- return;
- }
- } while (loop && item->state != SOUP_MESSAGE_FINISHED);
-}
-
-static void
-run_queue (SoupSessionAsync *sa)
-{
- SoupSession *session = SOUP_SESSION (sa);
- SoupMessageQueue *queue = soup_session_get_queue (session);
- SoupMessageQueueItem *item;
- SoupMessage *msg;
- gboolean try_pruning = TRUE, should_prune = FALSE;
-
- g_object_ref (session);
- soup_session_cleanup_connections (session, FALSE);
-
- try_again:
- for (item = soup_message_queue_first (queue);
- item;
- item = soup_message_queue_next (queue, item)) {
- msg = item->msg;
-
- /* CONNECT messages are handled specially */
- if (msg->method != SOUP_METHOD_CONNECT)
- process_queue_item (item, &should_prune, TRUE);
- }
-
- if (try_pruning && should_prune) {
- /* There is at least one message in the queue that
- * could be sent if we pruned an idle connection from
- * some other server.
- */
- if (soup_session_cleanup_connections (session, TRUE)) {
- try_pruning = should_prune = FALSE;
- goto try_again;
- }
- }
-
- g_object_unref (session);
-}
-
-static gboolean
-idle_run_queue (gpointer user_data)
-{
- SoupSessionAsyncPrivate *priv = user_data;
-
- if (priv->disposed)
- return FALSE;
-
- /* Ensure that the source is destroyed before running the queue */
- g_source_destroy (g_main_current_source ());
-
- run_queue (priv->sa);
- return FALSE;
-}
-
-static void
-do_idle_run_queue (SoupSession *session)
-{
- SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (session);
- GMainContext *async_context = soup_session_get_async_context (session);
- GSource *source;
-
- if (priv->disposed)
- return;
-
- /* We use priv rather than session as the source data, because
- * other parts of libsoup (or the calling app) may have sources
- * using the session as the source data.
- */
-
- source = g_main_context_find_source_by_user_data (async_context, priv);
- if (source)
- return;
-
- source = soup_add_completion (async_context, idle_run_queue, priv);
-}
-
-static void
-soup_session_async_queue_message (SoupSession *session, SoupMessage *req,
- SoupSessionCallback callback, gpointer user_data)
-{
- SoupMessageQueueItem *item;
-
- item = soup_session_append_queue_item (session, req, callback, user_data);
- soup_message_queue_item_unref (item);
-
- do_idle_run_queue (session);
-}
-
static guint
-soup_session_async_send_message (SoupSession *session, SoupMessage *req)
+soup_session_async_send_message (SoupSession *session, SoupMessage *msg)
{
SoupMessageQueueItem *item;
GMainContext *async_context =
soup_session_get_async_context (session);
- soup_session_async_queue_message (session, req, NULL, NULL);
-
- item = soup_message_queue_lookup (soup_session_get_queue (session), req);
- g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
+ item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
+ NULL, NULL);
+ soup_session_kick_queue (session);
while (item->state != SOUP_MESSAGE_FINISHED)
g_main_context_iteration (async_context, TRUE);
soup_message_queue_item_unref (item);
- return req->status_code;
+ return msg->status_code;
}
static void
@@ -430,7 +106,6 @@ soup_session_async_cancel_message (SoupSession *session, SoupMessage *msg,
{
SoupMessageQueue *queue;
SoupMessageQueueItem *item;
- gboolean dummy;
SOUP_SESSION_CLASS (soup_session_async_parent_class)->
cancel_message (session, msg, status_code);
@@ -452,304 +127,17 @@ soup_session_async_cancel_message (SoupSession *session, SoupMessage *msg,
item->state = SOUP_MESSAGE_FINISHING;
if (item->state != SOUP_MESSAGE_FINISHED)
- process_queue_item (item, &dummy, FALSE);
-
- soup_message_queue_item_unref (item);
-}
-
-static void
-got_passwords (SoupPasswordManager *password_manager, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying, gpointer session)
-{
- soup_session_unpause_message (session, msg);
- SOUP_SESSION_CLASS (soup_session_async_parent_class)->
- auth_required (session, msg, auth, retrying);
- g_object_unref (auth);
-}
-
-static void
-soup_session_async_auth_required (SoupSession *session, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying)
-{
- SoupSessionFeature *password_manager;
-
- password_manager = soup_session_get_feature_for_message (
- session, SOUP_TYPE_PASSWORD_MANAGER, msg);
- if (password_manager) {
- soup_session_pause_message (session, msg);
- g_object_ref (auth);
- soup_password_manager_get_passwords_async (
- SOUP_PASSWORD_MANAGER (password_manager),
- msg, auth, retrying,
- soup_session_get_async_context (session),
- NULL, /* FIXME cancellable */
- got_passwords, session);
- } else {
- SOUP_SESSION_CLASS (soup_session_async_parent_class)->
- auth_required (session, msg, auth, retrying);
- }
-}
-
-static void
-soup_session_async_kick (SoupSession *session)
-{
- do_idle_run_queue (session);
-}
-
-
-static void
-send_request_return_result (SoupMessageQueueItem *item,
- gpointer stream, GError *error)
-{
- GSimpleAsyncResult *simple;
-
- simple = item->result;
- item->result = NULL;
-
- if (error)
- g_simple_async_result_take_error (simple, error);
- else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
- if (stream)
- g_object_unref (stream);
- g_simple_async_result_set_error (simple,
- SOUP_HTTP_ERROR,
- item->msg->status_code,
- "%s",
- item->msg->reason_phrase);
- } else
- g_simple_async_result_set_op_res_gpointer (simple, stream, g_object_unref);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static void
-send_request_restarted (SoupSession *session, SoupMessageQueueItem *item)
-{
- /* We won't be needing this, then. */
- g_object_set_data (G_OBJECT (item->msg), "SoupSessionAsync:ostream", NULL);
- item->io_started = FALSE;
-}
-
-static void
-send_request_finished (SoupSession *session, SoupMessageQueueItem *item)
-{
- GMemoryOutputStream *mostream;
- GInputStream *istream = NULL;
- GError *error = NULL;
-
- if (!item->result) {
- /* Something else already took care of it. */
- return;
- }
-
- mostream = g_object_get_data (G_OBJECT (item->msg), "SoupSessionAsync:ostream");
- if (mostream) {
- gpointer data;
- gssize size;
-
- /* We thought it would be requeued, but it wasn't, so
- * return the original body.
- */
- size = g_memory_output_stream_get_data_size (mostream);
- data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
- istream = g_memory_input_stream_new_from_data (data, size, g_free);
- } else if (item->io_started) {
- /* The message finished before becoming readable. This
- * will happen, eg, if it's cancelled from got-headers.
- * Do nothing; the op will complete via read_ready_cb()
- * after we return;
- */
- return;
- } else {
- /* The message finished before even being started;
- * probably a tunnel connect failure.
- */
- istream = g_memory_input_stream_new ();
- }
-
- send_request_return_result (item, istream, error);
-}
-
-static void
-send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
- GInputStream *istream = g_object_get_data (source, "istream");
-
- GError *error = NULL;
-
- /* If the message was cancelled, it will be completed via other means */
- if (g_cancellable_is_cancelled (item->cancellable) ||
- !item->result) {
- soup_message_queue_item_unref (item);
- return;
- }
-
- if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
- result, &error) == -1) {
- send_request_return_result (item, NULL, error);
- return;
- }
+ soup_session_process_queue_item (session, item, NULL, FALSE);
- /* Otherwise either restarted or finished will eventually be called.
- * It should be safe to call the sync close() method here since
- * the message body has already been written.
- */
- g_input_stream_close (istream, NULL, NULL);
- do_idle_run_queue (item->session);
soup_message_queue_item_unref (item);
}
static void
-send_async_maybe_complete (SoupMessageQueueItem *item,
- GInputStream *stream)
-{
- if (item->msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
- item->msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED ||
- soup_session_would_redirect (item->session, item->msg)) {
- GOutputStream *ostream;
-
- /* Message may be requeued, so gather the current message body... */
- ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
- g_object_set_data_full (G_OBJECT (item->msg), "SoupSessionAsync:ostream",
- ostream, g_object_unref);
-
- g_object_set_data_full (G_OBJECT (ostream), "istream",
- stream, g_object_unref);
-
- /* Give the splice op its own ref on item */
- soup_message_queue_item_ref (item);
- g_output_stream_splice_async (ostream, stream,
- /* We can't use CLOSE_SOURCE because it
- * might get closed in the wrong thread.
- */
- G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- G_PRIORITY_DEFAULT,
- item->cancellable,
- send_async_spliced, item);
- return;
- }
-
- send_request_return_result (item, stream, NULL);
-}
-
-static void try_run_until_read (SoupMessageQueueItem *item);
-
-static gboolean
-read_ready_cb (SoupMessage *msg, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- try_run_until_read (item);
- return FALSE;
-}
-
-static void
-try_run_until_read (SoupMessageQueueItem *item)
-{
- GError *error = NULL;
- GInputStream *stream = NULL;
- GSource *source;
-
- if (soup_message_io_run_until_read (item->msg, item->cancellable, &error))
- stream = soup_message_io_get_response_istream (item->msg, &error);
- if (stream) {
- send_async_maybe_complete (item, stream);
- return;
- }
-
- if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
- item->state = SOUP_MESSAGE_RESTARTING;
- soup_message_io_finished (item->msg);
- g_error_free (error);
- return;
- }
-
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
- send_request_return_result (item, NULL, error);
- return;
- }
-
- g_clear_error (&error);
- source = soup_message_io_get_source (item->msg, item->cancellable,
- read_ready_cb, item);
- g_source_attach (source, soup_session_get_async_context (item->session));
- g_source_unref (source);
-}
-
-static void
-send_request_running (SoupSession *session, SoupMessageQueueItem *item)
-{
- item->io_started = TRUE;
- try_run_until_read (item);
-}
-
-void
-soup_session_send_request_async (SoupSession *session,
- SoupMessage *msg,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SoupMessageQueueItem *item;
- gboolean use_thread_context;
-
- g_return_if_fail (SOUP_IS_SESSION_ASYNC (session));
-
- g_object_get (G_OBJECT (session),
- SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
- NULL);
- g_return_if_fail (use_thread_context);
-
- soup_session_async_queue_message (session, msg, NULL, NULL);
-
- item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
- g_return_if_fail (item != NULL);
-
- item->new_api = TRUE;
- item->result = g_simple_async_result_new (G_OBJECT (session),
- callback, user_data,
- soup_session_send_request_async);
- g_simple_async_result_set_op_res_gpointer (item->result, item, (GDestroyNotify) soup_message_queue_item_unref);
-
- if (cancellable) {
- g_object_unref (item->cancellable);
- item->cancellable = g_object_ref (cancellable);
- }
-}
-
-GInputStream *
-soup_session_send_request_finish (SoupSession *session,
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple;
-
- g_return_val_if_fail (SOUP_IS_SESSION_ASYNC (session), NULL);
- g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (session), soup_session_send_request_async), NULL);
-
- simple = G_SIMPLE_ASYNC_RESULT (result);
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
-}
-
-static void
soup_session_async_class_init (SoupSessionAsyncClass *soup_session_async_class)
{
SoupSessionClass *session_class = SOUP_SESSION_CLASS (soup_session_async_class);
- GObjectClass *object_class = G_OBJECT_CLASS (session_class);
-
- g_type_class_add_private (soup_session_async_class,
- sizeof (SoupSessionAsyncPrivate));
/* virtual method override */
- session_class->queue_message = soup_session_async_queue_message;
session_class->send_message = soup_session_async_send_message;
session_class->cancel_message = soup_session_async_cancel_message;
- session_class->auth_required = soup_session_async_auth_required;
- session_class->kick = soup_session_async_kick;
-
- object_class->dispose = soup_session_async_dispose;
}
diff --git a/libsoup/soup-session-async.h b/libsoup/soup-session-async.h
index 9fb9cfe0..96217638 100644
--- a/libsoup/soup-session-async.h
+++ b/libsoup/soup-session-async.h
@@ -35,9 +35,11 @@ typedef struct {
GType soup_session_async_get_type (void);
+#ifndef SOUP_DISABLE_DEPRECATED
SoupSession *soup_session_async_new (void);
SoupSession *soup_session_async_new_with_options (const char *optname1,
...) G_GNUC_NULL_TERMINATED;
+#endif
G_END_DECLS
diff --git a/libsoup/soup-session-feature.c b/libsoup/soup-session-feature.c
index 9f9b235a..560bb8f7 100644
--- a/libsoup/soup-session-feature.c
+++ b/libsoup/soup-session-feature.c
@@ -52,7 +52,7 @@
* Since: 2.24
**/
-static void soup_session_feature_default_init (SoupSessionFeatureInterface *interface);
+static void soup_session_feature_default_init (SoupSessionFeatureInterface *iface);
G_DEFINE_INTERFACE (SoupSessionFeature, soup_session_feature, G_TYPE_OBJECT)
@@ -142,10 +142,10 @@ soup_session_feature_detach (SoupSessionFeature *feature,
}
static void
-soup_session_feature_default_init (SoupSessionFeatureInterface *interface)
+soup_session_feature_default_init (SoupSessionFeatureInterface *iface)
{
- interface->attach = soup_session_feature_real_attach;
- interface->detach = soup_session_feature_real_detach;
+ iface->attach = soup_session_feature_real_attach;
+ iface->detach = soup_session_feature_real_detach;
}
/**
diff --git a/libsoup/soup-session-feature.h b/libsoup/soup-session-feature.h
index aa64aa0f..962c8e11 100644
--- a/libsoup/soup-session-feature.h
+++ b/libsoup/soup-session-feature.h
@@ -46,17 +46,23 @@ typedef struct {
} SoupSessionFeatureInterface;
+SOUP_AVAILABLE_IN_2_24
GType soup_session_feature_get_type (void);
+SOUP_AVAILABLE_IN_2_24
void soup_session_feature_attach (SoupSessionFeature *feature,
SoupSession *session);
+SOUP_AVAILABLE_IN_2_24
void soup_session_feature_detach (SoupSessionFeature *feature,
SoupSession *session);
+SOUP_AVAILABLE_IN_2_34
gboolean soup_session_feature_add_feature (SoupSessionFeature *feature,
GType type);
+SOUP_AVAILABLE_IN_2_34
gboolean soup_session_feature_remove_feature (SoupSessionFeature *feature,
GType type);
+SOUP_AVAILABLE_IN_2_34
gboolean soup_session_feature_has_feature (SoupSessionFeature *feature,
GType type);
diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h
index b5e24e33..dc4d300b 100644
--- a/libsoup/soup-session-private.h
+++ b/libsoup/soup-session-private.h
@@ -17,23 +17,12 @@ SoupMessageQueue *soup_session_get_queue (SoupSession *s
SoupMessageQueueItem *soup_session_append_queue_item (SoupSession *session,
SoupMessage *msg,
+ gboolean async,
+ gboolean new_api,
SoupSessionCallback callback,
gpointer user_data);
-SoupMessageQueueItem *soup_session_make_connect_message (SoupSession *session,
- SoupConnection *conn);
-gboolean soup_session_get_connection (SoupSession *session,
- SoupMessageQueueItem *item,
- gboolean *try_pruning);
-gboolean soup_session_cleanup_connections (SoupSession *session,
- gboolean prune_idle);
-void soup_session_send_queue_item (SoupSession *session,
- SoupMessageQueueItem *item,
- SoupMessageCompletionFn completion_cb);
-void soup_session_unqueue_item (SoupSession *session,
- SoupMessageQueueItem *item);
-void soup_session_set_item_status (SoupSession *session,
- SoupMessageQueueItem *item,
- guint status_code);
+
+void soup_session_kick_queue (SoupSession *session);
GInputStream *soup_session_send_request (SoupSession *session,
SoupMessage *msg,
@@ -49,6 +38,11 @@ GInputStream *soup_session_send_request_finish (SoupSession *s
GAsyncResult *result,
GError **error);
+void soup_session_process_queue_item (SoupSession *session,
+ SoupMessageQueueItem *item,
+ gboolean *should_prune,
+ gboolean loop);
+
G_END_DECLS
#endif /* SOUP_SESSION_PRIVATE_H */
diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c
index 96ab9165..374f22bd 100644
--- a/libsoup/soup-session-sync.c
+++ b/libsoup/soup-session-sync.c
@@ -9,8 +9,6 @@
#include <config.h>
#endif
-#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-
#include "soup-session-sync.h"
#include "soup.h"
#include "soup-session-private.h"
@@ -19,64 +17,35 @@
/**
* SECTION:soup-session-sync
- * @short_description: Soup session for blocking I/O in multithreaded
- * programs.
+ * @short_description: (Deprecated) SoupSession for blocking I/O in
+ * multithreaded programs.
*
* #SoupSessionSync is an implementation of #SoupSession that uses
* synchronous I/O, intended for use in multi-threaded programs.
*
- * You can use #SoupSessionSync from multiple threads concurrently.
- * Eg, you can send a #SoupMessage in one thread, and then while
- * waiting for the response, send another #SoupMessage from another
- * thread. You can also send a message from one thread and then call
- * soup_session_cancel_message() on it from any other thread (although
- * you need to be careful to avoid race conditions, where the message
- * finishes and is then unreffed by the sending thread just before you
- * cancel it).
- *
- * However, the majority of other types and methods in libsoup are not
- * MT-safe. In particular, you <emphasis>cannot</emphasis> modify or
- * examine a #SoupMessage while it is being transmitted by
- * #SoupSessionSync in another thread. Once a message has been handed
- * off to #SoupSessionSync, it can only be manipulated from its signal
- * handler callbacks, until I/O is complete.
+ * As of libsoup 2.42, this is deprecated in favor of the plain
+ * #SoupSession class (which uses both asynchronous and synchronous
+ * I/O, depending on the API used). See the <link
+ * linkend="libsoup-session-porting">porting guide</link>.
**/
-typedef struct {
- GMutex lock;
- GCond cond;
-} SoupSessionSyncPrivate;
-#define SOUP_SESSION_SYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncPrivate))
-
G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
static void
soup_session_sync_init (SoupSessionSync *ss)
{
- SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (ss);
-
- g_mutex_init (&priv->lock);
- g_cond_init (&priv->cond);
-}
-
-static void
-soup_session_sync_finalize (GObject *object)
-{
- SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (object);
-
- g_mutex_clear (&priv->lock);
- g_cond_clear (&priv->cond);
-
- G_OBJECT_CLASS (soup_session_sync_parent_class)->finalize (object);
}
-
/**
* soup_session_sync_new:
*
* Creates an synchronous #SoupSession with the default options.
*
* Return value: the new session.
+ *
+ * Deprecated: #SoupSessionSync is deprecated; use a plain
+ * #SoupSession, created with soup_session_new(). See the <link
+ * linkend="libsoup-session-porting">porting guide</link>.
**/
SoupSession *
soup_session_sync_new (void)
@@ -92,6 +61,10 @@ soup_session_sync_new (void)
* Creates an synchronous #SoupSession with the specified options.
*
* Return value: the new session.
+ *
+ * Deprecated: #SoupSessionSync is deprecated; use a plain
+ * #SoupSession, created with soup_session_new_with_options(). See the
+ * <link linkend="libsoup-session-porting">porting guide</link>.
**/
SoupSession *
soup_session_sync_new_with_options (const char *optname1, ...)
@@ -107,197 +80,12 @@ soup_session_sync_new_with_options (const char *optname1, ...)
return session;
}
-static guint
-tunnel_connect (SoupSession *session, SoupMessageQueueItem *related)
-{
- SoupConnection *conn = related->conn;
- SoupMessageQueueItem *item;
- guint status;
-
- g_object_ref (conn);
-
- item = soup_session_make_connect_message (session, conn);
- do {
- soup_session_send_queue_item (session, item, NULL);
- status = item->msg->status_code;
- if (item->state == SOUP_MESSAGE_RESTARTING &&
- soup_message_io_in_progress (item->msg)) {
- soup_message_restarted (item->msg);
- item->state = SOUP_MESSAGE_RUNNING;
- } else {
- if (item->state == SOUP_MESSAGE_RESTARTING)
- status = SOUP_STATUS_TRY_AGAIN;
- item->state = SOUP_MESSAGE_FINISHED;
- soup_message_finished (item->msg);
- }
- } while (item->state == SOUP_MESSAGE_STARTING);
- soup_session_unqueue_item (session, item);
- soup_message_queue_item_unref (item);
-
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- if (!soup_connection_start_ssl_sync (conn, related->cancellable))
- status = SOUP_STATUS_SSL_FAILED;
- soup_message_set_https_status (related->msg, conn);
- }
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (status))
- soup_connection_disconnect (conn);
-
- g_object_unref (conn);
- return status;
-}
-
-static void
-get_connection (SoupMessageQueueItem *item)
-{
- SoupSession *session = item->session;
- SoupMessage *msg = item->msg;
- gboolean try_pruning = FALSE;
- guint status;
-
-try_again:
- soup_session_cleanup_connections (session, FALSE);
-
- if (!soup_session_get_connection (session, item, &try_pruning)) {
- if (!try_pruning)
- return;
- soup_session_cleanup_connections (session, TRUE);
- if (!soup_session_get_connection (session, item, &try_pruning))
- return;
- try_pruning = FALSE;
- }
-
- if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
- item->state = SOUP_MESSAGE_READY;
- return;
- }
-
- status = soup_connection_connect_sync (item->conn, item->cancellable);
- if (status == SOUP_STATUS_TRY_AGAIN) {
- soup_connection_disconnect (item->conn);
- soup_message_queue_item_set_connection (item, NULL);
- goto try_again;
- }
-
- soup_message_set_https_status (msg, item->conn);
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- if (!msg->status_code)
- soup_session_set_item_status (session, item, status);
- item->state = SOUP_MESSAGE_FINISHING;
- soup_connection_disconnect (item->conn);
- soup_message_queue_item_set_connection (item, NULL);
- return;
- }
-
- if (soup_connection_is_tunnelled (item->conn)) {
- status = tunnel_connect (session, item);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- soup_connection_disconnect (item->conn);
- soup_message_queue_item_set_connection (item, NULL);
- if (status == SOUP_STATUS_TRY_AGAIN)
- goto try_again;
- soup_session_set_item_status (session, item, status);
- item->state = SOUP_MESSAGE_FINISHING;
- return;
- }
- }
-
- item->state = SOUP_MESSAGE_READY;
-}
-
-static void process_queue_item (SoupMessageQueueItem *item);
-
-static void
-new_api_message_completed (SoupMessage *msg, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- if (item->state != SOUP_MESSAGE_RESTARTING) {
- item->state = SOUP_MESSAGE_FINISHING;
- process_queue_item (item);
- }
-}
-
-static void
-process_queue_item (SoupMessageQueueItem *item)
-{
- SoupSession *session = item->session;
- SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
-
- soup_message_queue_item_ref (item);
-
- do {
- if (item->paused) {
- g_mutex_lock (&priv->lock);
- while (item->paused)
- g_cond_wait (&priv->cond, &priv->lock);
- g_mutex_unlock (&priv->lock);
- }
-
- switch (item->state) {
- case SOUP_MESSAGE_STARTING:
- item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
- break;
-
- case SOUP_MESSAGE_AWAITING_CONNECTION:
- g_mutex_lock (&priv->lock);
- do {
- get_connection (item);
- if (item->state == SOUP_MESSAGE_AWAITING_CONNECTION)
- g_cond_wait (&priv->cond, &priv->lock);
- } while (item->state == SOUP_MESSAGE_AWAITING_CONNECTION);
- g_mutex_unlock (&priv->lock);
- break;
-
- case SOUP_MESSAGE_READY:
- item->state = SOUP_MESSAGE_RUNNING;
-
- if (item->new_api) {
- soup_session_send_queue_item (item->session, item, new_api_message_completed);
- goto out;
- }
-
- soup_session_send_queue_item (item->session, item, NULL);
- if (item->state != SOUP_MESSAGE_RESTARTING)
- item->state = SOUP_MESSAGE_FINISHING;
- break;
-
- case SOUP_MESSAGE_RUNNING:
- g_warn_if_fail (item->new_api);
- item->state = SOUP_MESSAGE_FINISHING;
- break;
-
- case SOUP_MESSAGE_RESTARTING:
- item->state = SOUP_MESSAGE_STARTING;
- soup_message_restarted (item->msg);
- break;
-
- case SOUP_MESSAGE_FINISHING:
- item->state = SOUP_MESSAGE_FINISHED;
- soup_message_finished (item->msg);
- soup_session_unqueue_item (session, item);
- g_cond_broadcast (&priv->cond);
- break;
-
- default:
- g_warn_if_reached ();
- item->state = SOUP_MESSAGE_FINISHING;
- break;
- }
- } while (item->state != SOUP_MESSAGE_FINISHED);
-
- out:
- soup_message_queue_item_unref (item);
-}
-
static gboolean
queue_message_callback (gpointer data)
{
SoupMessageQueueItem *item = data;
item->callback (item->session, item->msg, item->callback_data);
- g_object_unref (item->session);
soup_message_queue_item_unref (item);
return FALSE;
}
@@ -307,14 +95,12 @@ queue_message_thread (gpointer data)
{
SoupMessageQueueItem *item = data;
- process_queue_item (item);
+ soup_session_process_queue_item (item->session, item, NULL, TRUE);
if (item->callback) {
soup_add_completion (soup_session_get_async_context (item->session),
queue_message_callback, item);
- } else {
- g_object_unref (item->session);
+ } else
soup_message_queue_item_unref (item);
- }
return NULL;
}
@@ -326,234 +112,18 @@ soup_session_sync_queue_message (SoupSession *session, SoupMessage *msg,
SoupMessageQueueItem *item;
GThread *thread;
- g_object_ref (session);
- item = soup_session_append_queue_item (session, msg, callback, user_data);
+ item = soup_session_append_queue_item (session, msg, FALSE, FALSE,
+ callback, user_data);
thread = g_thread_new ("SoupSessionSync:queue_message",
queue_message_thread, item);
g_thread_unref (thread);
}
-static guint
-soup_session_sync_send_message (SoupSession *session, SoupMessage *msg)
-{
- SoupMessageQueueItem *item;
- guint status;
-
- item = soup_session_append_queue_item (session, msg, NULL, NULL);
- process_queue_item (item);
- status = msg->status_code;
- soup_message_queue_item_unref (item);
- return status;
-}
-
-static void
-soup_session_sync_cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
-{
- SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
-
- g_mutex_lock (&priv->lock);
- SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg, status_code);
- g_cond_broadcast (&priv->cond);
- g_mutex_unlock (&priv->lock);
-}
-
-static void
-soup_session_sync_auth_required (SoupSession *session, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying)
-{
- SoupSessionFeature *password_manager;
-
- password_manager = soup_session_get_feature_for_message (
- session, SOUP_TYPE_PASSWORD_MANAGER, msg);
- if (password_manager) {
- soup_password_manager_get_passwords_sync (
- SOUP_PASSWORD_MANAGER (password_manager),
- msg, auth, NULL); /* FIXME cancellable */
- }
-
- SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
- auth_required (session, msg, auth, retrying);
-}
-
-static void
-soup_session_sync_flush_queue (SoupSession *session)
-{
- SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
- SoupMessageQueue *queue;
- SoupMessageQueueItem *item;
- GHashTable *current;
- gboolean done = FALSE;
-
- /* Record the current contents of the queue */
- current = g_hash_table_new (NULL, NULL);
- queue = soup_session_get_queue (session);
- for (item = soup_message_queue_first (queue);
- item;
- item = soup_message_queue_next (queue, item))
- g_hash_table_insert (current, item, item);
-
- /* Cancel everything */
- SOUP_SESSION_CLASS (soup_session_sync_parent_class)->flush_queue (session);
-
- /* Wait until all of the items in @current have been removed
- * from the queue. (This is not the same as "wait for the
- * queue to be empty", because the app may queue new requests
- * in response to the cancellation of the old ones. We don't
- * try to cancel those requests as well, since we'd likely
- * just end up looping forever.)
- */
- g_mutex_lock (&priv->lock);
- do {
- done = TRUE;
- for (item = soup_message_queue_first (queue);
- item;
- item = soup_message_queue_next (queue, item)) {
- if (g_hash_table_lookup (current, item))
- done = FALSE;
- }
-
- if (!done)
- g_cond_wait (&priv->cond, &priv->lock);
- } while (!done);
- g_mutex_unlock (&priv->lock);
-
- g_hash_table_destroy (current);
-}
-
-static void
-soup_session_sync_kick (SoupSession *session)
-{
- SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
-
- g_mutex_lock (&priv->lock);
- g_cond_broadcast (&priv->cond);
- g_mutex_unlock (&priv->lock);
-}
-
static void
soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
{
- GObjectClass *object_class = G_OBJECT_CLASS (session_sync_class);
SoupSessionClass *session_class = SOUP_SESSION_CLASS (session_sync_class);
- g_type_class_add_private (session_sync_class, sizeof (SoupSessionSyncPrivate));
-
/* virtual method override */
session_class->queue_message = soup_session_sync_queue_message;
- session_class->send_message = soup_session_sync_send_message;
- session_class->cancel_message = soup_session_sync_cancel_message;
- session_class->auth_required = soup_session_sync_auth_required;
- session_class->flush_queue = soup_session_sync_flush_queue;
- session_class->kick = soup_session_sync_kick;
-
- object_class->finalize = soup_session_sync_finalize;
-}
-
-
-GInputStream *
-soup_session_send_request (SoupSession *session,
- SoupMessage *msg,
- GCancellable *cancellable,
- GError **error)
-{
- SoupMessageQueueItem *item;
- GInputStream *stream = NULL;
- GOutputStream *ostream;
- GMemoryOutputStream *mostream;
- gssize size;
- GError *my_error = NULL;
-
- g_return_val_if_fail (SOUP_IS_SESSION_SYNC (session), NULL);
-
- item = soup_session_append_queue_item (session, msg, NULL, NULL);
-
- item->new_api = TRUE;
- if (cancellable) {
- g_object_unref (item->cancellable);
- item->cancellable = g_object_ref (cancellable);
- }
-
- while (!stream) {
- /* Get a connection, etc */
- process_queue_item (item);
- if (item->state != SOUP_MESSAGE_RUNNING)
- break;
-
- /* Send request, read headers */
- if (!soup_message_io_run_until_read (msg, item->cancellable, &my_error)) {
- if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
- item->state = SOUP_MESSAGE_RESTARTING;
- soup_message_io_finished (item->msg);
- g_clear_error (&my_error);
- continue;
- } else
- break;
- }
-
- stream = soup_message_io_get_response_istream (msg, &my_error);
- if (!stream)
- break;
-
- /* Break if the message doesn't look likely-to-be-requeued */
- if (msg->status_code != SOUP_STATUS_UNAUTHORIZED &&
- msg->status_code != SOUP_STATUS_PROXY_UNAUTHORIZED &&
- !soup_session_would_redirect (session, msg))
- break;
-
- /* Gather the current message body... */
- ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
- if (g_output_stream_splice (ostream, stream,
- G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
- G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- item->cancellable, &my_error) == -1) {
- g_object_unref (stream);
- g_object_unref (ostream);
- stream = NULL;
- break;
- }
- g_object_unref (stream);
- stream = NULL;
-
- /* If the message was requeued, loop */
- if (item->state == SOUP_MESSAGE_RESTARTING) {
- g_object_unref (ostream);
- continue;
- }
-
- /* Not requeued, so return the original body */
- mostream = G_MEMORY_OUTPUT_STREAM (ostream);
- size = g_memory_output_stream_get_data_size (mostream);
- stream = g_memory_input_stream_new ();
- if (size) {
- g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
- g_memory_output_stream_steal_data (mostream),
- size, g_free);
- }
- g_object_unref (ostream);
- }
-
- if (my_error)
- g_propagate_error (error, my_error);
- else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
- if (stream) {
- g_object_unref (stream);
- stream = NULL;
- }
- g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
- msg->reason_phrase);
- } else if (!stream)
- stream = g_memory_input_stream_new ();
-
- if (!stream) {
- if (soup_message_io_in_progress (msg))
- soup_message_io_finished (msg);
- else if (item->state != SOUP_MESSAGE_FINISHED)
- item->state = SOUP_MESSAGE_FINISHING;
-
- if (item->state != SOUP_MESSAGE_FINISHED)
- process_queue_item (item);
- }
-
- soup_message_queue_item_unref (item);
- return stream;
}
diff --git a/libsoup/soup-session-sync.h b/libsoup/soup-session-sync.h
index 845e01c3..cf81f6b0 100644
--- a/libsoup/soup-session-sync.h
+++ b/libsoup/soup-session-sync.h
@@ -35,9 +35,11 @@ typedef struct {
GType soup_session_sync_get_type (void);
+#ifndef SOUP_DISABLE_DEPRECATED
SoupSession *soup_session_sync_new (void);
SoupSession *soup_session_sync_new_with_options (const char *optname1,
...) G_GNUC_NULL_TERMINATED;
+#endif
G_END_DECLS
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 7b68d427..132554d6 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -13,13 +13,13 @@
#include "soup-session.h"
#include "soup.h"
-#include "soup-auth-manager-ntlm.h"
+#include "soup-auth-manager.h"
+#include "soup-cache-private.h"
#include "soup-connection.h"
-#include "soup-marshal.h"
#include "soup-message-private.h"
#include "soup-misc-private.h"
#include "soup-message-queue.h"
-#include "soup-proxy-resolver-static.h"
+#include "soup-proxy-resolver-wrapper.h"
#include "soup-session-private.h"
#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
@@ -31,7 +31,8 @@
* #SoupSession is the object that controls client-side HTTP. A
* #SoupSession encapsulates all of the state that libsoup is keeping
* on behalf of your program; cached HTTP connections, authentication
- * information, etc.
+ * information, etc. It also keeps track of various global options
+ * and features that you are using.
*
* Most applications will only need a single #SoupSession; the primary
* reason you might need multiple sessions is if you need to have
@@ -42,12 +43,16 @@
* one session for the first user, and a second session for the other
* user.)
*
- * #SoupSession itself is an abstract class, with two subclasses. If
- * you are using the glib main loop, you will generally want to use
- * #SoupSessionAsync, which uses non-blocking I/O and callbacks. On
- * the other hand, if your application is threaded and you want to do
- * synchronous I/O in a separate thread from the UI, use
- * #SoupSessionSync.
+ * In the past, #SoupSession was an abstract class, and users needed
+ * to choose between #SoupSessionAsync (which always uses
+ * #GMainLoop<!-- -->-based I/O), or #SoupSessionSync (which always uses
+ * blocking I/O and can be used from multiple threads simultaneously).
+ * This is no longer necessary; you can (and should) use a plain
+ * #SoupSession, which supports both synchronous and asynchronous use.
+ * (When using a plain #SoupSession, soup_session_queue_message()
+ * behaves like it traditionally did on a #SoupSessionAsync, and
+ * soup_session_send_message() behaves like it traditionally did on a
+ * #SoupSessionSync.)
**/
static void
@@ -74,12 +79,16 @@ typedef struct {
SoupSession *session;
} SoupSessionHost;
static guint soup_host_uri_hash (gconstpointer key);
-gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
+static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
typedef struct {
+ SoupSession *session;
+ gboolean disposed;
+
GTlsDatabase *tlsdb;
char *ssl_ca_file;
gboolean ssl_strict;
+ gboolean tlsdb_use_default;
SoupMessageQueue *queue;
@@ -96,37 +105,60 @@ typedef struct {
guint max_conns, max_conns_per_host;
guint io_timeout, idle_timeout;
- /* Must hold the conn_lock before potentially creating a
- * new SoupSessionHost, or adding/removing a connection.
- * Must not emit signals or destroy objects while holding it.
+ SoupAddress *local_addr;
+
+ /* Must hold the conn_lock before potentially creating a new
+ * SoupSessionHost, adding/removing a connection,
+ * disconnecting a connection, or moving a connection from
+ * IDLE to IN_USE. Must not emit signals or destroy objects
+ * while holding it. conn_cond is signaled when it may be
+ * possible for a previously-blocked message to continue.
*/
GMutex conn_lock;
+ GCond conn_cond;
GMainContext *async_context;
gboolean use_thread_context;
+ GSList *run_queue_sources;
GResolver *resolver;
+ GProxyResolver *proxy_resolver;
+ gboolean proxy_use_default;
+ SoupURI *proxy_uri;
char **http_aliases, **https_aliases;
+
+ GHashTable *request_types;
} SoupSessionPrivate;
#define SOUP_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION, SoupSessionPrivate))
+#define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
+
static void free_host (SoupSessionHost *host);
+static void connection_state_changed (GObject *object, GParamSpec *param,
+ gpointer user_data);
+static void connection_disconnected (SoupConnection *conn, gpointer user_data);
+static void drop_connection (SoupSession *session, SoupSessionHost *host,
+ SoupConnection *conn);
static void auth_manager_authenticate (SoupAuthManager *manager,
SoupMessage *msg, SoupAuth *auth,
gboolean retrying, gpointer user_data);
+static void async_run_queue (SoupSession *session);
+
+static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
+
#define SOUP_SESSION_MAX_CONNS_DEFAULT 10
#define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
-#define SOUP_SESSION_MAX_REDIRECTION_COUNT 20
+#define SOUP_SESSION_MAX_RESEND_COUNT 20
#define SOUP_SESSION_USER_AGENT_BASE "libsoup/" PACKAGE_VERSION
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
- soup_init ();
- )
+G_DEFINE_TYPE_WITH_CODE (SoupSession, soup_session, G_TYPE_OBJECT,
+ soup_init ();
+ )
enum {
REQUEST_QUEUED,
@@ -144,6 +176,7 @@ enum {
PROP_0,
PROP_PROXY_URI,
+ PROP_PROXY_RESOLVER,
PROP_MAX_CONNS,
PROP_MAX_CONNS_PER_HOST,
PROP_USE_NTLM,
@@ -163,6 +196,7 @@ enum {
PROP_REMOVE_FEATURE_BY_TYPE,
PROP_HTTP_ALIASES,
PROP_HTTPS_ALIASES,
+ PROP_LOCAL_ADDRESS,
LAST_PROP
};
@@ -173,9 +207,12 @@ soup_session_init (SoupSession *session)
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupAuthManager *auth_manager;
+ priv->session = session;
+
priv->queue = soup_message_queue_new (session);
g_mutex_init (&priv->conn_lock);
+ g_cond_init (&priv->conn_cond);
priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
soup_host_uri_equal,
NULL, (GDestroyNotify)free_host);
@@ -189,7 +226,7 @@ soup_session_init (SoupSession *session)
priv->features_cache = g_hash_table_new (NULL, NULL);
- auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER_NTLM, NULL);
+ auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER, NULL);
g_signal_connect (auth_manager, "authenticate",
G_CALLBACK (auth_manager_authenticate), session);
soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
@@ -209,6 +246,49 @@ soup_session_init (SoupSession *session)
priv->http_aliases = g_new (char *, 2);
priv->http_aliases[0] = (char *)g_intern_string ("*");
priv->http_aliases[1] = NULL;
+
+ priv->request_types = g_hash_table_new (soup_str_case_hash,
+ soup_str_case_equal);
+ soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_HTTP);
+ soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_FILE);
+ soup_session_add_feature_by_type (session, SOUP_TYPE_REQUEST_DATA);
+}
+
+static GObject *
+soup_session_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+
+ object = G_OBJECT_CLASS (soup_session_parent_class)->constructor (type, n_construct_properties, construct_params);
+
+ /* If this is a "plain" SoupSession, fix up the default
+ * properties values, etc.
+ */
+ if (type == SOUP_TYPE_SESSION) {
+ SoupSession *session = SOUP_SESSION (object);
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ g_clear_pointer (&priv->async_context, g_main_context_unref);
+ priv->async_context = g_main_context_ref_thread_default ();
+ priv->use_thread_context = TRUE;
+
+ priv->io_timeout = priv->idle_timeout = 60;
+
+ priv->http_aliases[0] = NULL;
+
+ /* If the user overrides the proxy or tlsdb during construction,
+ * we don't want to needlessly resolve the extension point. So
+ * we just set flags saying to do it later.
+ */
+ priv->proxy_use_default = TRUE;
+ priv->tlsdb_use_default = TRUE;
+
+ soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
+ }
+
+ return object;
}
static void
@@ -216,8 +296,19 @@ soup_session_dispose (GObject *object)
{
SoupSession *session = SOUP_SESSION (object);
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ GSList *iter;
+ priv->disposed = TRUE;
+
+ for (iter = priv->run_queue_sources; iter; iter = iter->next) {
+ g_source_destroy (iter->data);
+ g_source_unref (iter->data);
+ }
+ g_clear_pointer (&priv->run_queue_sources, g_slist_free);
+
+ priv->disposed = TRUE;
soup_session_abort (session);
+ g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
while (priv->features)
soup_session_remove_feature (session, priv->features->data);
@@ -234,6 +325,7 @@ soup_session_finalize (GObject *object)
soup_message_queue_destroy (priv->queue);
g_mutex_clear (&priv->conn_lock);
+ g_cond_clear (&priv->conn_cond);
g_hash_table_destroy (priv->http_hosts);
g_hash_table_destroy (priv->https_hosts);
g_hash_table_destroy (priv->conns);
@@ -245,14 +337,19 @@ soup_session_finalize (GObject *object)
g_free (priv->ssl_ca_file);
g_clear_pointer (&priv->async_context, g_main_context_unref);
+ g_clear_object (&priv->local_addr);
g_hash_table_destroy (priv->features_cache);
g_object_unref (priv->resolver);
+ g_clear_object (&priv->proxy_resolver);
+ g_clear_pointer (&priv->proxy_uri, soup_uri_free);
g_free (priv->http_aliases);
g_free (priv->https_aliases);
+ g_hash_table_destroy (priv->request_types);
+
G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
}
@@ -342,16 +439,19 @@ set_tlsdb (SoupSession *session, GTlsDatabase *tlsdb)
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
GTlsDatabase *system_default;
+ priv->tlsdb_use_default = FALSE;
if (tlsdb == priv->tlsdb)
return;
g_object_freeze_notify (G_OBJECT (session));
system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
- if (priv->tlsdb == system_default || tlsdb == system_default) {
- g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
+ if (system_default) {
+ if (priv->tlsdb == system_default || tlsdb == system_default) {
+ g_object_notify (G_OBJECT (session), "ssl-use-system-ca-file");
+ }
+ g_object_unref (system_default);
}
- g_object_unref (system_default);
if (priv->ssl_ca_file) {
g_free (priv->ssl_ca_file);
@@ -375,6 +475,8 @@ set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
GTlsDatabase *system_default;
+ priv->tlsdb_use_default = FALSE;
+
system_default = g_tls_backend_get_default_database (g_tls_backend_get_default ());
if (use_system_ca_file)
@@ -382,7 +484,7 @@ set_use_system_ca_file (SoupSession *session, gboolean use_system_ca_file)
else if (priv->tlsdb == system_default)
set_tlsdb (session, NULL);
- g_object_unref (system_default);
+ g_clear_object (&system_default);
}
static void
@@ -392,6 +494,7 @@ set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
GTlsDatabase *tlsdb;
GError *error = NULL;
+ priv->tlsdb_use_default = FALSE;
if (!g_strcmp0 (priv->ssl_ca_file, ssl_ca_file))
return;
@@ -406,6 +509,7 @@ set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
path = g_build_filename (cwd, ssl_ca_file, NULL);
tlsdb = g_tls_file_database_new (path, &error);
g_free (path);
+ g_free (cwd);
}
if (error) {
@@ -419,10 +523,15 @@ set_ssl_ca_file (SoupSession *session, const char *ssl_ca_file)
}
set_tlsdb (session, tlsdb);
- g_object_unref (tlsdb);
+ if (tlsdb) {
+ g_object_unref (tlsdb);
- priv->ssl_ca_file = g_strdup (ssl_ca_file);
- g_object_notify (G_OBJECT (session), "ssl-ca-file");
+ priv->ssl_ca_file = g_strdup (ssl_ca_file);
+ g_object_notify (G_OBJECT (session), "ssl-ca-file");
+ } else if (priv->ssl_ca_file) {
+ g_clear_pointer (&priv->ssl_ca_file, g_free);
+ g_object_notify (G_OBJECT (session), "ssl-ca-file");
+ }
g_object_thaw_notify (G_OBJECT (session));
}
@@ -451,29 +560,60 @@ set_aliases (char ***variable, char **value)
}
static void
+set_proxy_resolver (SoupSession *session, SoupURI *uri,
+ SoupProxyURIResolver *soup_resolver,
+ GProxyResolver *g_resolver)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_URI_RESOLVER);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+ g_clear_object (&priv->proxy_resolver);
+ g_clear_pointer (&priv->proxy_uri, soup_uri_free);
+ priv->proxy_use_default = FALSE;
+
+ if (uri) {
+ char *uri_string;
+
+ priv->proxy_uri = soup_uri_copy (uri);
+ uri_string = soup_uri_to_string_internal (uri, FALSE, TRUE);
+ priv->proxy_resolver = g_simple_proxy_resolver_new (uri_string, NULL);
+ g_free (uri_string);
+ } else if (soup_resolver) {
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ if (SOUP_IS_PROXY_RESOLVER_DEFAULT (soup_resolver))
+ priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
+ else
+ priv->proxy_resolver = soup_proxy_resolver_wrapper_new (soup_resolver);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+ } else if (g_resolver)
+ priv->proxy_resolver = g_object_ref (g_resolver);
+}
+
+static void
soup_session_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
SoupSession *session = SOUP_SESSION (object);
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
- SoupURI *uri;
const char *user_agent;
SoupSessionFeature *feature;
+ GMainContext *async_context;
switch (prop_id) {
+ case PROP_LOCAL_ADDRESS:
+ priv->local_addr = g_value_dup_object (value);
+ break;
case PROP_PROXY_URI:
- uri = g_value_get_boxed (value);
-
- if (uri) {
- soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
- feature = SOUP_SESSION_FEATURE (soup_proxy_resolver_static_new (uri));
- soup_session_add_feature (session, feature);
- g_object_unref (feature);
- } else
- soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
-
+ set_proxy_resolver (session, g_value_get_boxed (value),
+ NULL, NULL);
soup_session_abort (session);
break;
+ case PROP_PROXY_RESOLVER:
+ set_proxy_resolver (session, NULL, NULL,
+ g_value_get_object (value));
+ break;
case PROP_MAX_CONNS:
priv->max_conns = g_value_get_int (value);
break;
@@ -481,7 +621,8 @@ soup_session_set_property (GObject *object, guint prop_id,
priv->max_conns_per_host = g_value_get_int (value);
break;
case PROP_USE_NTLM:
- feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
+ g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
+ feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
if (feature) {
if (g_value_get_boolean (value))
soup_session_feature_add_feature (feature, SOUP_TYPE_AUTH_NTLM);
@@ -503,11 +644,16 @@ soup_session_set_property (GObject *object, guint prop_id,
priv->ssl_strict = g_value_get_boolean (value);
break;
case PROP_ASYNC_CONTEXT:
- priv->async_context = g_value_get_pointer (value);
+ async_context = g_value_get_pointer (value);
+ if (async_context && async_context != g_main_context_get_thread_default ())
+ g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
+ priv->async_context = async_context;
if (priv->async_context)
g_main_context_ref (priv->async_context);
break;
case PROP_USE_THREAD_CONTEXT:
+ if (!g_value_get_boolean (value))
+ g_return_if_fail (!SOUP_IS_PLAIN_SESSION (session));
priv->use_thread_context = g_value_get_boolean (value);
if (priv->use_thread_context) {
if (priv->async_context)
@@ -575,6 +721,30 @@ soup_session_set_property (GObject *object, guint prop_id,
}
}
+static GProxyResolver *
+get_proxy_resolver (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ if (priv->proxy_use_default) {
+ priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
+ priv->proxy_use_default = FALSE;
+ }
+ return priv->proxy_resolver;
+}
+
+static GTlsDatabase *
+get_tls_database (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ if (priv->tlsdb_use_default) {
+ priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+ priv->tlsdb_use_default = FALSE;
+ }
+ return priv->tlsdb;
+}
+
static void
soup_session_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
@@ -585,14 +755,14 @@ soup_session_get_property (GObject *object, guint prop_id,
GTlsDatabase *tlsdb;
switch (prop_id) {
+ case PROP_LOCAL_ADDRESS:
+ g_value_set_object (value, priv->local_addr);
+ break;
case PROP_PROXY_URI:
- feature = soup_session_get_feature (session, SOUP_TYPE_PROXY_RESOLVER_STATIC);
- if (feature) {
- g_object_get_property (G_OBJECT (feature),
- SOUP_PROXY_RESOLVER_STATIC_PROXY_URI,
- value);
- } else
- g_value_set_boxed (value, NULL);
+ g_value_set_boxed (value, priv->proxy_uri);
+ break;
+ case PROP_PROXY_RESOLVER:
+ g_value_set_object (value, get_proxy_resolver (session));
break;
case PROP_MAX_CONNS:
g_value_set_int (value, priv->max_conns);
@@ -601,7 +771,7 @@ soup_session_get_property (GObject *object, guint prop_id,
g_value_set_int (value, priv->max_conns_per_host);
break;
case PROP_USE_NTLM:
- feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER_NTLM);
+ feature = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
if (feature)
g_value_set_boolean (value, soup_session_feature_has_feature (feature, SOUP_TYPE_AUTH_NTLM));
else
@@ -612,11 +782,11 @@ soup_session_get_property (GObject *object, guint prop_id,
break;
case PROP_SSL_USE_SYSTEM_CA_FILE:
tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
- g_value_set_boolean (value, priv->tlsdb == tlsdb);
- g_object_unref (tlsdb);
+ g_value_set_boolean (value, get_tls_database (session) == tlsdb);
+ g_clear_object (&tlsdb);
break;
case PROP_TLS_DATABASE:
- g_value_set_object (value, priv->tlsdb);
+ g_value_set_object (value, get_tls_database (session));
break;
case PROP_SSL_STRICT:
g_value_set_boolean (value, priv->ssl_strict);
@@ -654,59 +824,57 @@ soup_session_get_property (GObject *object, guint prop_id,
}
}
-static gboolean
-uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
+/**
+ * soup_session_new:
+ *
+ * Creates a #SoupSession with the default options.
+ *
+ * Return value: the new session.
+ *
+ * Since: 2.42
+ */
+SoupSession *
+soup_session_new (void)
{
- int i;
-
- if (uri->scheme == SOUP_URI_SCHEME_HTTP)
- return TRUE;
- else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
- return FALSE;
- else if (!priv->http_aliases)
- return FALSE;
-
- for (i = 0; priv->http_aliases[i]; i++) {
- if (uri->scheme == priv->http_aliases[i])
- return TRUE;
- }
-
- if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
- return TRUE;
- else
- return FALSE;
+ return g_object_new (SOUP_TYPE_SESSION, NULL);
}
-static gboolean
-uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
+/**
+ * soup_session_new_with_options:
+ * @optname1: name of first property to set
+ * @...: value of @optname1, followed by additional property/value pairs
+ *
+ * Creates a #SoupSession with the specified options.
+ *
+ * Return value: the new session.
+ *
+ * Since: 2.42
+ */
+SoupSession *
+soup_session_new_with_options (const char *optname1,
+ ...)
{
- int i;
-
- if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
- return TRUE;
- else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
- return FALSE;
- else if (!priv->https_aliases)
- return FALSE;
+ SoupSession *session;
+ va_list ap;
- for (i = 0; priv->https_aliases[i]; i++) {
- if (uri->scheme == priv->https_aliases[i])
- return TRUE;
- }
+ va_start (ap, optname1);
+ session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION,
+ optname1, ap);
+ va_end (ap);
- return FALSE;
+ return session;
}
/**
* soup_session_get_async_context:
* @session: a #SoupSession
*
- * Gets @session's async_context. This does not add a ref to the
- * context, so you will need to ref it yourself if you want it to
- * outlive its session.
+ * Gets @session's #SoupSession:async-context. This does not add a ref
+ * to the context, so you will need to ref it yourself if you want it
+ * to outlive its session.
*
- * If #SoupSession:use-thread-context is true, this will return the
- * current thread-default main context.
+ * For a modern #SoupSession, this will always just return the
+ * thread-default #GMainContext, and so is not especially useful.
*
* Return value: (transfer none): @session's #GMainContext, which may
* be %NULL
@@ -727,6 +895,10 @@ soup_session_get_async_context (SoupSession *session)
/* Hosts */
+/* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
+ * because we want to ignore the protocol; http://example.com and
+ * webcal://example.com are the same host.
+ */
static guint
soup_host_uri_hash (gconstpointer key)
{
@@ -737,7 +909,7 @@ soup_host_uri_hash (gconstpointer key)
return uri->port + soup_str_case_hash (uri->host);
}
-gboolean
+static gboolean
soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
{
const SoupURI *one = v1;
@@ -764,7 +936,7 @@ soup_session_host_new (SoupSession *session, SoupURI *uri)
host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
- if (uri_is_https (priv, host->uri))
+ if (soup_uri_is_https (host->uri, priv->https_aliases))
host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
else
host->uri->scheme = SOUP_URI_SCHEME_HTTP;
@@ -788,7 +960,7 @@ get_host_for_uri (SoupSession *session, SoupURI *uri)
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupSessionHost *host;
- if (uri_is_https (priv, uri))
+ if (soup_uri_is_https (uri, priv->https_aliases))
host = g_hash_table_lookup (priv->https_hosts, uri);
else
host = g_hash_table_lookup (priv->http_hosts, uri);
@@ -797,7 +969,7 @@ get_host_for_uri (SoupSession *session, SoupURI *uri)
host = soup_session_host_new (session, uri);
- if (uri_is_https (priv, uri))
+ if (soup_uri_is_https (uri, priv->https_aliases))
g_hash_table_insert (priv->https_hosts, host->uri, host);
else
g_hash_table_insert (priv->http_hosts, host->uri, host);
@@ -805,10 +977,7 @@ get_host_for_uri (SoupSession *session, SoupURI *uri)
return host;
}
-/* Note: get_host_for_message doesn't lock the conn_lock. The caller
- * must do it itself if there's a chance the host doesn't already
- * exist.
- */
+/* Requires conn_lock to be locked */
static SoupSessionHost *
get_host_for_message (SoupSession *session, SoupMessage *msg)
{
@@ -818,8 +987,7 @@ get_host_for_message (SoupSession *session, SoupMessage *msg)
static void
free_host (SoupSessionHost *host)
{
- g_slist_free_full (host->connections,
- (GDestroyNotify) soup_connection_disconnect);
+ g_warn_if_fail (host->connections == NULL);
if (host->keep_alive_src) {
g_source_destroy (host->keep_alive_src);
@@ -832,19 +1000,11 @@ free_host (SoupSessionHost *host)
}
static void
-soup_session_real_auth_required (SoupSession *session, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying)
-{
- g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
-}
-
-static void
auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
SoupAuth *auth, gboolean retrying,
gpointer session)
{
- SOUP_SESSION_GET_CLASS (session)->auth_required (
- session, msg, auth, retrying);
+ g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
}
#define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
@@ -911,7 +1071,8 @@ soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
if (!new_uri)
return FALSE;
if (!new_uri->host || !*new_uri->host ||
- (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
+ (!soup_uri_is_http (new_uri, priv->http_aliases) &&
+ !soup_uri_is_https (new_uri, priv->https_aliases))) {
soup_uri_free (new_uri);
return FALSE;
}
@@ -947,27 +1108,12 @@ soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
gboolean
soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
{
- SoupMessageQueueItem *item;
SoupURI *new_uri;
new_uri = redirection_uri (msg);
if (!new_uri)
return FALSE;
- item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
- if (!item) {
- soup_uri_free (new_uri);
- return FALSE;
- }
- if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
- soup_uri_free (new_uri);
- soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
- soup_message_queue_item_unref (item);
- return FALSE;
- }
- item->redirection_count++;
- soup_message_queue_item_unref (item);
-
if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
if (msg->method != SOUP_METHOD_HEAD) {
g_object_set (msg,
@@ -1010,15 +1156,67 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
soup_session_redirect_message (session, msg);
}
+static void
+re_emit_connection_event (SoupConnection *conn,
+ GSocketClientEvent event,
+ GIOStream *connection,
+ gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ soup_message_network_event (item->msg, event, connection);
+}
+
+static void
+soup_session_set_item_connection (SoupSession *session,
+ SoupMessageQueueItem *item,
+ SoupConnection *conn)
+{
+ if (item->conn) {
+ g_signal_handlers_disconnect_by_func (item->conn, re_emit_connection_event, item);
+ g_object_unref (item->conn);
+ }
+
+ item->conn = conn;
+ soup_message_set_connection (item->msg, conn);
+
+ if (item->conn) {
+ g_object_ref (item->conn);
+ g_signal_connect (item->conn, "event",
+ G_CALLBACK (re_emit_connection_event), item);
+ }
+}
+
+static void
+message_restarted (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ if (item->conn &&
+ (!soup_message_is_keepalive (msg) ||
+ SOUP_STATUS_IS_REDIRECTION (msg->status_code))) {
+ if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
+ soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
+ soup_session_set_item_connection (item->session, item, NULL);
+ }
+
+ soup_message_cleanup_response (msg);
+}
+
SoupMessageQueueItem *
soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
+ gboolean async, gboolean new_api,
SoupSessionCallback callback, gpointer user_data)
{
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupMessageQueueItem *item;
SoupSessionHost *host;
+ soup_message_cleanup_response (msg);
+
item = soup_message_queue_append (priv->queue, msg, callback, user_data);
+ item->async = async;
+ item->new_api = new_api;
g_mutex_lock (&priv->conn_lock);
host = get_host_for_message (session, item->msg);
@@ -1030,6 +1228,8 @@ soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
msg, "got_body", "Location",
G_CALLBACK (redirect_handler), item);
}
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (message_restarted), item);
g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
@@ -1037,7 +1237,7 @@ soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
return item;
}
-void
+static void
soup_session_send_queue_item (SoupSession *session,
SoupMessageQueueItem *item,
SoupMessageCompletionFn completion_cb)
@@ -1076,9 +1276,9 @@ soup_session_send_queue_item (SoupSession *session,
soup_connection_send_request (item->conn, item, completion_cb, item);
}
-gboolean
+static gboolean
soup_session_cleanup_connections (SoupSession *session,
- gboolean prune_idle)
+ gboolean cleanup_idle)
{
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
GSList *conns = NULL, *c;
@@ -1091,8 +1291,11 @@ soup_session_cleanup_connections (SoupSession *session,
while (g_hash_table_iter_next (&iter, &conn, &host)) {
state = soup_connection_get_state (conn);
if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
- (prune_idle && state == SOUP_CONNECTION_IDLE))
+ (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
conns = g_slist_prepend (conns, g_object_ref (conn));
+ g_hash_table_iter_remove (&iter);
+ drop_connection (session, host, conn);
+ }
}
g_mutex_unlock (&priv->conn_lock);
@@ -1116,6 +1319,15 @@ free_unused_host (gpointer user_data)
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session);
g_mutex_lock (&priv->conn_lock);
+
+ /* In a multithreaded session, a connection might have been
+ * added while we were waiting for conn_lock.
+ */
+ if (host->connections) {
+ g_mutex_unlock (&priv->conn_lock);
+ return FALSE;
+ }
+
/* This will free the host in addition to removing it from the
* hash table
*/
@@ -1129,17 +1341,15 @@ free_unused_host (gpointer user_data)
}
static void
-connection_disconnected (SoupConnection *conn, gpointer user_data)
+drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
{
- SoupSession *session = user_data;
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
- SoupSessionHost *host;
- g_mutex_lock (&priv->conn_lock);
+ /* Note: caller must hold conn_lock, and must remove @conn
+ * from priv->conns itself.
+ */
- host = g_hash_table_lookup (priv->conns, conn);
if (host) {
- g_hash_table_remove (priv->conns, conn);
host->connections = g_slist_remove (host->connections, conn);
host->num_conns--;
@@ -1161,106 +1371,430 @@ connection_disconnected (SoupConnection *conn, gpointer user_data)
}
g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
+ g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
priv->num_conns--;
- g_mutex_unlock (&priv->conn_lock);
g_object_unref (conn);
}
-SoupMessageQueueItem *
-soup_session_make_connect_message (SoupSession *session,
- SoupConnection *conn)
+static void
+connection_disconnected (SoupConnection *conn, gpointer user_data)
+{
+ SoupSession *session = user_data;
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupSessionHost *host;
+
+ g_mutex_lock (&priv->conn_lock);
+
+ host = g_hash_table_lookup (priv->conns, conn);
+ if (host)
+ g_hash_table_remove (priv->conns, conn);
+ drop_connection (session, host, conn);
+
+ g_mutex_unlock (&priv->conn_lock);
+
+ soup_session_kick_queue (session);
+}
+
+static void
+connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
+{
+ SoupSession *session = user_data;
+ SoupConnection *conn = SOUP_CONNECTION (object);
+
+ if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE)
+ soup_session_kick_queue (session);
+}
+
+SoupMessageQueue *
+soup_session_get_queue (SoupSession *session)
{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ return priv->queue;
+}
+
+static void
+soup_session_unqueue_item (SoupSession *session,
+ SoupMessageQueueItem *item)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupSessionHost *host;
+
+ if (item->conn) {
+ if (item->msg->method != SOUP_METHOD_CONNECT ||
+ !SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code))
+ soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
+ soup_session_set_item_connection (session, item, NULL);
+ }
+
+ if (item->state != SOUP_MESSAGE_FINISHED) {
+ g_warning ("finished an item with state %d", item->state);
+ return;
+ }
+
+ soup_message_queue_remove (priv->queue, item);
+
+ g_mutex_lock (&priv->conn_lock);
+ host = get_host_for_message (session, item->msg);
+ host->num_messages--;
+ g_cond_broadcast (&priv->conn_cond);
+ g_mutex_unlock (&priv->conn_lock);
+
+ /* g_signal_handlers_disconnect_by_func doesn't work if you
+ * have a metamarshal, meaning it doesn't work with
+ * soup_message_add_header_handler()
+ */
+ g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, item);
+ g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
+ soup_message_queue_item_unref (item);
+}
+
+static void
+soup_session_set_item_status (SoupSession *session,
+ SoupMessageQueueItem *item,
+ guint status_code,
+ GError *error)
+{
+ SoupURI *uri = NULL;
+
+ switch (status_code) {
+ case SOUP_STATUS_CANT_RESOLVE:
+ case SOUP_STATUS_CANT_CONNECT:
+ uri = soup_message_get_uri (item->msg);
+ break;
+
+ case SOUP_STATUS_CANT_RESOLVE_PROXY:
+ case SOUP_STATUS_CANT_CONNECT_PROXY:
+ if (item->conn)
+ uri = soup_connection_get_proxy_uri (item->conn);
+ break;
+
+ case SOUP_STATUS_SSL_FAILED:
+ if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
+ soup_message_set_status_full (item->msg, status_code,
+ "TLS/SSL support not available; install glib-networking");
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (error)
+ soup_message_set_status_full (item->msg, status_code, error->message);
+ else if (uri && uri->host) {
+ char *msg = g_strdup_printf ("%s (%s)",
+ soup_status_get_phrase (status_code),
+ uri->host);
+ soup_message_set_status_full (item->msg, status_code, msg);
+ g_free (msg);
+ } else
+ soup_message_set_status (item->msg, status_code);
+}
+
+
+static void
+message_completed (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ if (item->async)
+ soup_session_kick_queue (item->session);
+
+ if (item->state != SOUP_MESSAGE_RESTARTING) {
+ item->state = SOUP_MESSAGE_FINISHING;
+
+ if (item->new_api && !item->async)
+ soup_session_process_queue_item (item->session, item, NULL, TRUE);
+ }
+}
+
+static guint
+status_from_connect_error (SoupMessageQueueItem *item, GError *error)
+{
+ guint status;
+
+ if (!error)
+ return SOUP_STATUS_OK;
+
+ if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (item->session);
+ SoupSessionHost *host;
+
+ g_mutex_lock (&priv->conn_lock);
+ host = get_host_for_message (item->session, item->msg);
+ if (!host->ssl_fallback) {
+ host->ssl_fallback = TRUE;
+ status = SOUP_STATUS_TRY_AGAIN;
+ } else
+ status = SOUP_STATUS_SSL_FAILED;
+ g_mutex_unlock (&priv->conn_lock);
+ } else if (error->domain == G_TLS_ERROR)
+ status = SOUP_STATUS_SSL_FAILED;
+ else if (error->domain == G_RESOLVER_ERROR)
+ status = SOUP_STATUS_CANT_RESOLVE;
+ else if (error->domain == G_IO_ERROR) {
+ if (error->code == G_IO_ERROR_CANCELLED)
+ status = SOUP_STATUS_CANCELLED;
+ else if (error->code == G_IO_ERROR_HOST_UNREACHABLE ||
+ error->code == G_IO_ERROR_NETWORK_UNREACHABLE ||
+ error->code == G_IO_ERROR_CONNECTION_REFUSED)
+ status = SOUP_STATUS_CANT_CONNECT;
+ else if (error->code == G_IO_ERROR_PROXY_FAILED ||
+ error->code == G_IO_ERROR_PROXY_AUTH_FAILED ||
+ error->code == G_IO_ERROR_PROXY_NEED_AUTH ||
+ error->code == G_IO_ERROR_PROXY_NOT_ALLOWED)
+ status = SOUP_STATUS_CANT_CONNECT_PROXY;
+ else
+ status = SOUP_STATUS_IO_ERROR;
+ } else
+ status = SOUP_STATUS_IO_ERROR;
+
+ if (item->conn && soup_connection_is_via_proxy (item->conn))
+ return soup_status_proxify (status);
+ else
+ return status;
+}
+
+static void
+tunnel_complete (SoupMessageQueueItem *tunnel_item,
+ guint status, GError *error)
+{
+ SoupMessageQueueItem *item = tunnel_item->related;
+ SoupSession *session = tunnel_item->session;
+
+ soup_message_finished (tunnel_item->msg);
+ soup_message_queue_item_unref (tunnel_item);
+
+ if (item->msg->status_code)
+ item->state = SOUP_MESSAGE_FINISHING;
+ soup_message_set_https_status (item->msg, item->conn);
+
+ item->error = error;
+ if (!status)
+ status = status_from_connect_error (item, error);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ soup_connection_disconnect (item->conn);
+ soup_session_set_item_connection (session, item, NULL);
+ if (!item->new_api || item->msg->status_code == 0)
+ soup_session_set_item_status (session, item, status, error);
+ }
+
+ item->state = SOUP_MESSAGE_READY;
+ if (item->async)
+ soup_session_kick_queue (session);
+ soup_message_queue_item_unref (item);
+}
+
+static void
+tunnel_handshake_complete (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SoupConnection *conn = SOUP_CONNECTION (object);
+ SoupMessageQueueItem *tunnel_item = user_data;
+ GError *error = NULL;
+
+ soup_connection_start_ssl_finish (conn, result, &error);
+ tunnel_complete (tunnel_item, 0, error);
+}
+
+static void
+tunnel_message_completed (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *tunnel_item = user_data;
+ SoupMessageQueueItem *item = tunnel_item->related;
+ SoupSession *session = tunnel_item->session;
+ guint status;
+
+ if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
+ soup_message_restarted (msg);
+ if (tunnel_item->conn) {
+ tunnel_item->state = SOUP_MESSAGE_RUNNING;
+ soup_session_send_queue_item (session, tunnel_item,
+ tunnel_message_completed);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
+ }
+
+ tunnel_item->state = SOUP_MESSAGE_FINISHED;
+ soup_session_unqueue_item (session, tunnel_item);
+
+ status = tunnel_item->msg->status_code;
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ tunnel_complete (tunnel_item, status, NULL);
+ return;
+ }
+
+ if (tunnel_item->async) {
+ soup_connection_start_ssl_async (item->conn, item->cancellable,
+ tunnel_handshake_complete,
+ tunnel_item);
+ } else {
+ GError *error = NULL;
+
+ soup_connection_start_ssl_sync (item->conn, item->cancellable, &error);
+ tunnel_complete (tunnel_item, 0, error);
+ }
+}
+
+static void
+tunnel_connect (SoupMessageQueueItem *item)
+{
+ SoupSession *session = item->session;
+ SoupMessageQueueItem *tunnel_item;
SoupURI *uri;
SoupMessage *msg;
- SoupMessageQueueItem *item;
- uri = soup_connection_get_remote_uri (conn);
+ item->state = SOUP_MESSAGE_TUNNELING;
+
+ uri = soup_connection_get_remote_uri (item->conn);
msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
- item = soup_session_append_queue_item (session, msg, NULL, NULL);
- soup_message_queue_item_set_connection (item, conn);
+ tunnel_item = soup_session_append_queue_item (session, msg,
+ item->async, FALSE,
+ NULL, NULL);
g_object_unref (msg);
- item->state = SOUP_MESSAGE_RUNNING;
+ tunnel_item->related = item;
+ soup_message_queue_item_ref (item);
+ soup_session_set_item_connection (session, tunnel_item, item->conn);
+ tunnel_item->state = SOUP_MESSAGE_RUNNING;
- g_signal_emit (session, signals[TUNNELING], 0, conn);
- return item;
+ g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
+
+ soup_session_send_queue_item (session, tunnel_item,
+ tunnel_message_completed);
}
-gboolean
-soup_session_get_connection (SoupSession *session,
- SoupMessageQueueItem *item,
- gboolean *try_pruning)
+static void
+connect_complete (SoupMessageQueueItem *item, SoupConnection *conn, GError *error)
+{
+ SoupSession *session = item->session;
+ guint status;
+
+ soup_message_set_https_status (item->msg, item->conn);
+
+ if (!error) {
+ item->state = SOUP_MESSAGE_CONNECTED;
+ return;
+ }
+
+ item->error = error;
+ status = status_from_connect_error (item, error);
+ soup_connection_disconnect (conn);
+ if (item->state == SOUP_MESSAGE_CONNECTING) {
+ if (!item->new_api || item->msg->status_code == 0)
+ soup_session_set_item_status (session, item, status, error);
+ soup_session_set_item_connection (session, item, NULL);
+ item->state = SOUP_MESSAGE_READY;
+ }
+}
+
+static void
+connect_async_complete (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SoupConnection *conn = SOUP_CONNECTION (object);
+ SoupMessageQueueItem *item = user_data;
+ GError *error = NULL;
+
+ soup_connection_connect_finish (conn, result, &error);
+ connect_complete (item, conn, error);
+
+ if (item->state == SOUP_MESSAGE_CONNECTED ||
+ item->state == SOUP_MESSAGE_READY)
+ async_run_queue (item->session);
+ else
+ soup_session_kick_queue (item->session);
+
+ soup_message_queue_item_unref (item);
+}
+
+/* requires conn_lock */
+static SoupConnection *
+get_connection_for_host (SoupSession *session,
+ SoupMessageQueueItem *item,
+ SoupSessionHost *host,
+ gboolean need_new_connection,
+ gboolean *try_cleanup)
{
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupConnection *conn;
- SoupSessionHost *host;
GSList *conns;
int num_pending = 0;
- gboolean need_new_connection;
+ GProxyResolver *proxy_resolver;
+ GTlsDatabase *tlsdb;
+
+ if (priv->disposed)
+ return FALSE;
if (item->conn) {
g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, FALSE);
- return TRUE;
+ return item->conn;
}
- need_new_connection =
- (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
- !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method);
-
- g_mutex_lock (&priv->conn_lock);
-
- host = get_host_for_message (session, item->msg);
for (conns = host->connections; conns; conns = conns->next) {
- if (!need_new_connection && soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
- soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
- g_mutex_unlock (&priv->conn_lock);
- soup_message_queue_item_set_connection (item, conns->data);
- soup_message_set_https_status (item->msg, item->conn);
- return TRUE;
- } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
+ conn = conns->data;
+
+ if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
+ soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+ return conn;
+ } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
num_pending++;
}
/* Limit the number of pending connections; num_messages / 2
* is somewhat arbitrary...
*/
- if (num_pending > host->num_messages / 2) {
- g_mutex_unlock (&priv->conn_lock);
- return FALSE;
- }
+ if (num_pending > host->num_messages / 2)
+ return NULL;
if (host->num_conns >= priv->max_conns_per_host) {
if (need_new_connection)
- *try_pruning = TRUE;
- g_mutex_unlock (&priv->conn_lock);
- return FALSE;
+ *try_cleanup = TRUE;
+ return NULL;
}
if (priv->num_conns >= priv->max_conns) {
- *try_pruning = TRUE;
- g_mutex_unlock (&priv->conn_lock);
- return FALSE;
+ *try_cleanup = TRUE;
+ return NULL;
}
+ proxy_resolver = get_proxy_resolver (session);
+ tlsdb = get_tls_database (session);
+
conn = g_object_new (
SOUP_TYPE_CONNECTION,
SOUP_CONNECTION_REMOTE_URI, host->uri,
- SOUP_CONNECTION_PROXY_RESOLVER, soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER),
- SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
- SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
- SOUP_CONNECTION_SSL_STRICT, (priv->tlsdb != NULL) && priv->ssl_strict,
+ SOUP_CONNECTION_PROXY_RESOLVER, proxy_resolver,
+ SOUP_CONNECTION_SSL, soup_uri_is_https (soup_message_get_uri (item->msg), priv->https_aliases),
+ SOUP_CONNECTION_SSL_CREDENTIALS, tlsdb,
+ SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict && (tlsdb != NULL || SOUP_IS_PLAIN_SESSION (session)),
SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
+ SOUP_CONNECTION_LOCAL_ADDRESS, priv->local_addr,
NULL);
g_signal_connect (conn, "disconnected",
G_CALLBACK (connection_disconnected),
session);
+ g_signal_connect (conn, "notify::state",
+ G_CALLBACK (connection_state_changed),
+ session);
+ /* This is a debugging-related signal, and so can ignore the
+ * usual rule about not emitting signals while holding
+ * conn_lock.
+ */
g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
g_hash_table_insert (priv->conns, conn, host);
@@ -1275,98 +1809,227 @@ soup_session_get_connection (SoupSession *session,
host->keep_alive_src = NULL;
}
- g_mutex_unlock (&priv->conn_lock);
- soup_message_queue_item_set_connection (item, conn);
- return TRUE;
+ return conn;
}
-SoupMessageQueue *
-soup_session_get_queue (SoupSession *session)
+static gboolean
+get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
{
+ SoupSession *session = item->session;
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupSessionHost *host;
+ SoupConnection *conn = NULL;
+ gboolean my_should_cleanup = FALSE;
+ gboolean need_new_connection;
- return priv->queue;
-}
+ soup_session_cleanup_connections (session, FALSE);
-void
-soup_session_unqueue_item (SoupSession *session,
- SoupMessageQueueItem *item)
-{
- SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
- SoupSessionHost *host;
+ need_new_connection =
+ (soup_message_get_flags (item->msg) & SOUP_MESSAGE_NEW_CONNECTION) ||
+ (!(soup_message_get_flags (item->msg) & SOUP_MESSAGE_IDEMPOTENT) &&
+ !SOUP_METHOD_IS_IDEMPOTENT (item->msg->method));
- if (item->conn) {
- if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE)
- soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
- soup_message_queue_item_set_connection (item, NULL);
+ g_mutex_lock (&priv->conn_lock);
+ host = get_host_for_message (session, item->msg);
+ while (TRUE) {
+ conn = get_connection_for_host (session, item, host,
+ need_new_connection,
+ &my_should_cleanup);
+ if (conn || item->async)
+ break;
+
+ if (my_should_cleanup) {
+ g_mutex_unlock (&priv->conn_lock);
+ soup_session_cleanup_connections (session, TRUE);
+ g_mutex_lock (&priv->conn_lock);
+
+ my_should_cleanup = FALSE;
+ continue;
+ }
+
+ g_cond_wait (&priv->conn_cond, &priv->conn_lock);
}
+ g_mutex_unlock (&priv->conn_lock);
- if (item->state != SOUP_MESSAGE_FINISHED) {
- g_warning ("finished an item with state %d", item->state);
- return;
+ if (!conn) {
+ if (should_cleanup)
+ *should_cleanup = my_should_cleanup;
+ return FALSE;
}
- soup_message_queue_remove (priv->queue, item);
+ soup_session_set_item_connection (session, item, conn);
- g_mutex_lock (&priv->conn_lock);
- host = get_host_for_message (session, item->msg);
- host->num_messages--;
- g_mutex_unlock (&priv->conn_lock);
+ if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
+ item->state = SOUP_MESSAGE_READY;
+ soup_message_set_https_status (item->msg, item->conn);
+ return TRUE;
+ }
- /* g_signal_handlers_disconnect_by_func doesn't work if you
- * have a metamarshal, meaning it doesn't work with
- * soup_message_add_header_handler()
- */
- g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, item);
- g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, item->msg);
- soup_message_queue_item_unref (item);
+ item->state = SOUP_MESSAGE_CONNECTING;
+
+ if (item->async) {
+ soup_message_queue_item_ref (item);
+ soup_connection_connect_async (item->conn, item->cancellable,
+ connect_async_complete, item);
+ return FALSE;
+ } else {
+ GError *error = NULL;
+
+ soup_connection_connect_sync (item->conn, item->cancellable, &error);
+ connect_complete (item, conn, error);
+
+ return TRUE;
+ }
}
void
-soup_session_set_item_status (SoupSession *session,
- SoupMessageQueueItem *item,
- guint status_code)
+soup_session_process_queue_item (SoupSession *session,
+ SoupMessageQueueItem *item,
+ gboolean *should_cleanup,
+ gboolean loop)
{
- SoupURI *uri;
- char *msg;
+ g_assert (item->session == session);
- switch (status_code) {
- case SOUP_STATUS_CANT_RESOLVE:
- case SOUP_STATUS_CANT_CONNECT:
- uri = soup_message_get_uri (item->msg);
- msg = g_strdup_printf ("%s (%s)",
- soup_status_get_phrase (status_code),
- uri->host);
- soup_message_set_status_full (item->msg, status_code, msg);
- g_free (msg);
- break;
+ do {
+ if (item->paused)
+ return;
- case SOUP_STATUS_CANT_RESOLVE_PROXY:
- case SOUP_STATUS_CANT_CONNECT_PROXY:
- if (item->proxy_uri && item->proxy_uri->host) {
- msg = g_strdup_printf ("%s (%s)",
- soup_status_get_phrase (status_code),
- item->proxy_uri->host);
- soup_message_set_status_full (item->msg, status_code, msg);
- g_free (msg);
+ switch (item->state) {
+ case SOUP_MESSAGE_STARTING:
+ if (!get_connection (item, should_cleanup))
+ return;
break;
+
+ case SOUP_MESSAGE_CONNECTED:
+ if (soup_connection_is_tunnelled (item->conn))
+ tunnel_connect (item);
+ else
+ item->state = SOUP_MESSAGE_READY;
+ break;
+
+ case SOUP_MESSAGE_READY:
+ if (item->msg->status_code) {
+ if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
+ soup_message_cleanup_response (item->msg);
+ item->state = SOUP_MESSAGE_STARTING;
+ } else
+ item->state = SOUP_MESSAGE_FINISHING;
+ break;
+ }
+
+ item->state = SOUP_MESSAGE_RUNNING;
+
+ soup_session_send_queue_item (session, item, message_completed);
+
+ if (item->new_api) {
+ if (item->async)
+ async_send_request_running (session, item);
+ return;
+ }
+ break;
+
+ case SOUP_MESSAGE_RUNNING:
+ if (item->async)
+ return;
+
+ g_warn_if_fail (item->new_api);
+ item->state = SOUP_MESSAGE_FINISHING;
+ break;
+
+ case SOUP_MESSAGE_CACHED:
+ /* Will be handled elsewhere */
+ return;
+
+ case SOUP_MESSAGE_RESTARTING:
+ item->state = SOUP_MESSAGE_STARTING;
+ soup_message_restarted (item->msg);
+ break;
+
+ case SOUP_MESSAGE_FINISHING:
+ item->state = SOUP_MESSAGE_FINISHED;
+ soup_message_finished (item->msg);
+ if (item->state != SOUP_MESSAGE_FINISHED) {
+ g_return_if_fail (!item->new_api);
+ break;
+ }
+
+ soup_message_queue_item_ref (item);
+ soup_session_unqueue_item (session, item);
+ if (item->async && item->callback)
+ item->callback (session, item->msg, item->callback_data);
+ soup_message_queue_item_unref (item);
+ return;
+
+ default:
+ /* Nothing to do with this message in any
+ * other state.
+ */
+ g_warn_if_fail (item->async);
+ return;
}
- soup_message_set_status (item->msg, status_code);
- break;
+ } while (loop && item->state != SOUP_MESSAGE_FINISHED);
+}
- case SOUP_STATUS_SSL_FAILED:
- if (!g_tls_backend_supports_tls (g_tls_backend_get_default ())) {
- soup_message_set_status_full (item->msg, status_code,
- "TLS/SSL support not available; install glib-networking");
- } else
- soup_message_set_status (item->msg, status_code);
- break;
+static void
+async_run_queue (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item;
+ SoupMessage *msg;
+ gboolean try_cleanup = TRUE, should_cleanup = FALSE;
- default:
- soup_message_set_status (item->msg, status_code);
- break;
+ g_object_ref (session);
+ soup_session_cleanup_connections (session, FALSE);
+
+ try_again:
+ for (item = soup_message_queue_first (priv->queue);
+ item;
+ item = soup_message_queue_next (priv->queue, item)) {
+ msg = item->msg;
+
+ /* CONNECT messages are handled specially */
+ if (msg->method == SOUP_METHOD_CONNECT)
+ continue;
+
+ if (item->async_context != soup_session_get_async_context (session))
+ continue;
+
+ soup_session_process_queue_item (session, item, &should_cleanup, TRUE);
}
+
+ if (try_cleanup && should_cleanup) {
+ /* There is at least one message in the queue that
+ * could be sent if we cleanupd an idle connection from
+ * some other server.
+ */
+ if (soup_session_cleanup_connections (session, TRUE)) {
+ try_cleanup = should_cleanup = FALSE;
+ goto try_again;
+ }
+ }
+
+ g_object_unref (session);
+}
+
+static gboolean
+idle_run_queue (gpointer user_data)
+{
+ SoupSessionPrivate *priv = user_data;
+ GSource *source;
+
+ if (priv->disposed)
+ return FALSE;
+
+ source = g_main_current_source ();
+ priv->run_queue_sources = g_slist_remove (priv->run_queue_sources, source);
+
+ /* Ensure that the source is destroyed before running the queue */
+ g_source_destroy (source);
+ g_source_unref (source);
+
+ g_assert (priv->session);
+ async_run_queue (priv->session);
+ return FALSE;
}
/**
@@ -1379,6 +2042,18 @@ soup_session_set_item_status (SoupSession *session,
* qv.
**/
+static void
+soup_session_real_queue_message (SoupSession *session, SoupMessage *msg,
+ SoupSessionCallback callback, gpointer user_data)
+{
+ SoupMessageQueueItem *item;
+
+ item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
+ callback, user_data);
+ soup_session_kick_queue (session);
+ soup_message_queue_item_unref (item);
+}
+
/**
* soup_session_queue_message:
* @session: a #SoupSession
@@ -1387,14 +2062,26 @@ soup_session_set_item_status (SoupSession *session,
* be called after the message completes or when an unrecoverable error occurs.
* @user_data: (allow-none): a pointer passed to @callback.
*
- * Queues the message @msg for sending. All messages are processed
- * while the glib main loop runs. If @msg has been processed before,
- * any resources related to the time it was last sent are freed.
+ * Queues the message @msg for asynchronously sending the request and
+ * receiving a response in the current thread-default #GMainContext.
+ * If @msg has been processed before, any resources related to the
+ * time it was last sent are freed.
*
* Upon message completion, the callback specified in @callback will
- * be invoked (in the thread associated with @session's async
- * context). If after returning from this callback the message has not
+ * be invoked. If after returning from this callback the message has not
* been requeued, @msg will be unreffed.
+ *
+ * (The behavior above applies to a plain #SoupSession; if you are
+ * using #SoupSessionAsync or #SoupSessionSync, then the #GMainContext
+ * that is used depends on the settings of #SoupSession:async-context
+ * and #SoupSession:use-thread-context, and for #SoupSessionSync, the
+ * message will actually be sent and processed in another thread, with
+ * only the final callback occurring in the indicated #GMainContext.)
+ *
+ * Contrast this method with soup_session_send_async(), which also
+ * asynchronously sends a message, but returns before reading the
+ * response body, and allows you to read the response via a
+ * #GInputStream.
*/
void
soup_session_queue_message (SoupSession *session, SoupMessage *msg,
@@ -1419,7 +2106,17 @@ soup_session_real_requeue_message (SoupSession *session, SoupMessage *msg)
item = soup_message_queue_lookup (priv->queue, msg);
g_return_if_fail (item != NULL);
- item->state = SOUP_MESSAGE_RESTARTING;
+
+ if (item->resend_count >= SOUP_SESSION_MAX_RESEND_COUNT) {
+ if (SOUP_STATUS_IS_REDIRECTION (msg->status_code))
+ soup_message_set_status (msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
+ else
+ g_warning ("SoupMessage %p stuck in infinite loop?", msg);
+ } else {
+ item->resend_count++;
+ item->state = SOUP_MESSAGE_RESTARTING;
+ }
+
soup_message_queue_item_unref (item);
}
@@ -1440,6 +2137,19 @@ soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
}
+static guint
+soup_session_real_send_message (SoupSession *session, SoupMessage *msg)
+{
+ SoupMessageQueueItem *item;
+ guint status;
+
+ item = soup_session_append_queue_item (session, msg, FALSE, FALSE,
+ NULL, NULL);
+ soup_session_process_queue_item (session, item, NULL, TRUE);
+ status = msg->status_code;
+ soup_message_queue_item_unref (item);
+ return status;
+}
/**
* soup_session_send_message:
@@ -1450,7 +2160,18 @@ soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
* transfer is finished successfully or there is an unrecoverable
* error.
*
- * @msg is not freed upon return.
+ * Unlike with soup_session_queue_message(), @msg is not freed upon
+ * return.
+ *
+ * (Note that if you call this method on a #SoupSessionAsync, it will
+ * still use asynchronous I/O internally, running the glib main loop
+ * to process the message, which may also cause other events to be
+ * processed.)
+ *
+ * Contrast this method with soup_session_send(), which also
+ * synchronously sends a message, but returns before reading the
+ * response body, and allows you to read the response via a
+ * #GInputStream.
*
* Return value: the HTTP status code of the response
*/
@@ -1471,6 +2192,9 @@ soup_session_send_message (SoupSession *session, SoupMessage *msg)
*
* Pauses HTTP I/O on @msg. Call soup_session_unpause_message() to
* resume I/O.
+ *
+ * This may only be called for asynchronous messages (those sent on a
+ * #SoupSessionAsync or using soup_session_queue_message()).
**/
void
soup_session_pause_message (SoupSession *session,
@@ -1485,6 +2209,7 @@ soup_session_pause_message (SoupSession *session,
priv = SOUP_SESSION_GET_PRIVATE (session);
item = soup_message_queue_lookup (priv->queue, msg);
g_return_if_fail (item != NULL);
+ g_return_if_fail (item->async);
item->paused = TRUE;
if (item->state == SOUP_MESSAGE_RUNNING)
@@ -1492,6 +2217,51 @@ soup_session_pause_message (SoupSession *session,
soup_message_queue_item_unref (item);
}
+static void
+soup_session_real_kick_queue (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item;
+ gboolean have_sync_items = FALSE;
+
+ if (priv->disposed)
+ return;
+
+ for (item = soup_message_queue_first (priv->queue);
+ item;
+ item = soup_message_queue_next (priv->queue, item)) {
+ if (item->async) {
+ GSource *source;
+
+ /* We use priv rather than session as the
+ * source data, because other parts of libsoup
+ * (or the calling app) may have sources using
+ * the session as the source data.
+ */
+ source = g_main_context_find_source_by_user_data (item->async_context, priv);
+ if (!source) {
+ source = soup_add_completion_reffed (item->async_context,
+ idle_run_queue, priv);
+ priv->run_queue_sources = g_slist_prepend (priv->run_queue_sources,
+ source);
+ }
+ } else
+ have_sync_items = TRUE;
+ }
+
+ if (have_sync_items) {
+ g_mutex_lock (&priv->conn_lock);
+ g_cond_broadcast (&priv->conn_cond);
+ g_mutex_unlock (&priv->conn_lock);
+ }
+}
+
+void
+soup_session_kick_queue (SoupSession *session)
+{
+ SOUP_SESSION_GET_CLASS (session)->kick (session);
+}
+
/**
* soup_session_unpause_message:
* @session: a #SoupSession
@@ -1503,6 +2273,9 @@ soup_session_pause_message (SoupSession *session,
* If @msg is being sent via blocking I/O, this will resume reading or
* writing immediately. If @msg is using non-blocking I/O, then
* reading or writing won't resume until you return to the main loop.
+ *
+ * This may only be called for asynchronous messages (those sent on a
+ * #SoupSessionAsync or using soup_session_queue_message()).
**/
void
soup_session_unpause_message (SoupSession *session,
@@ -1517,13 +2290,14 @@ soup_session_unpause_message (SoupSession *session,
priv = SOUP_SESSION_GET_PRIVATE (session);
item = soup_message_queue_lookup (priv->queue, msg);
g_return_if_fail (item != NULL);
+ g_return_if_fail (item->async);
item->paused = FALSE;
if (item->state == SOUP_MESSAGE_RUNNING)
soup_message_io_unpause (msg);
soup_message_queue_item_unref (item);
- SOUP_SESSION_GET_CLASS (session)->kick (session);
+ soup_session_kick_queue (session);
}
@@ -1540,6 +2314,7 @@ soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint
soup_message_set_status (msg, status_code);
g_cancellable_cancel (item->cancellable);
+ soup_session_kick_queue (item->session);
soup_message_queue_item_unref (item);
}
@@ -1555,7 +2330,7 @@ soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint
* may call this at any time after handing @msg off to @session; if
* @session has started sending the request but has not yet received
* the complete response, then it will close the request's connection.
- * Note that with non-idempotent requests (eg,
+ * Note that with requests that have side effects (eg,
* <literal>POST</literal>, <literal>PUT</literal>,
* <literal>DELETE</literal>) it is possible that you might cancel the
* request after the server acts on it, but before it returns a
@@ -1566,9 +2341,12 @@ soup_session_real_cancel_message (SoupSession *session, SoupMessage *msg, guint
* The response headers, on the other hand, will always be either
* empty or complete.
*
- * For messages queued with soup_session_queue_message() (and
- * cancelled from the same thread), the callback will be invoked
- * before soup_session_cancel_message() returns.
+ * Beware that with the deprecated #SoupSessionAsync, messages queued
+ * with soup_session_queue_message() will have their callbacks invoked
+ * before soup_session_cancel_message() returns. The plain
+ * #SoupSession does not have this behavior; cancelling an
+ * asynchronous message will merely queue its callback to be run after
+ * returning to the main loop.
**/
void
soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
@@ -1599,20 +2377,66 @@ soup_session_real_flush_queue (SoupSession *session)
{
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupMessageQueueItem *item;
+ GHashTable *current = NULL;
+ gboolean done = FALSE;
+
+ if (SOUP_IS_SESSION_SYNC (session)) {
+ /* Record the current contents of the queue */
+ current = g_hash_table_new (NULL, NULL);
+ for (item = soup_message_queue_first (priv->queue);
+ item;
+ item = soup_message_queue_next (priv->queue, item))
+ g_hash_table_insert (current, item, item);
+ }
+ /* Cancel everything */
for (item = soup_message_queue_first (priv->queue);
item;
item = soup_message_queue_next (priv->queue, item)) {
soup_session_cancel_message (session, item->msg,
SOUP_STATUS_CANCELLED);
}
+
+ if (SOUP_IS_SESSION_SYNC (session)) {
+ /* Wait until all of the items in @current have been
+ * removed from the queue. (This is not the same as
+ * "wait for the queue to be empty", because the app
+ * may queue new requests in response to the
+ * cancellation of the old ones. We don't try to
+ * cancel those requests as well, since we'd likely
+ * just end up looping forever.)
+ */
+ g_mutex_lock (&priv->conn_lock);
+ do {
+ done = TRUE;
+ for (item = soup_message_queue_first (priv->queue);
+ item;
+ item = soup_message_queue_next (priv->queue, item)) {
+ if (g_hash_table_lookup (current, item))
+ done = FALSE;
+ }
+
+ if (!done)
+ g_cond_wait (&priv->conn_cond, &priv->conn_lock);
+ } while (!done);
+ g_mutex_unlock (&priv->conn_lock);
+
+ g_hash_table_destroy (current);
+ }
}
/**
* soup_session_abort:
* @session: the session
*
- * Cancels all pending requests in @session.
+ * Cancels all pending requests in @session and closes all idle
+ * persistent connections.
+ *
+ * The message cancellation has the same semantics as with
+ * soup_session_cancel_message(); asynchronous requests on a
+ * #SoupSessionAsync will have their callback called before
+ * soup_session_abort() returns. Requests on a plain #SoupSession will
+ * not.
**/
void
soup_session_abort (SoupSession *session)
@@ -1631,8 +2455,11 @@ soup_session_abort (SoupSession *session)
g_mutex_lock (&priv->conn_lock);
conns = NULL;
g_hash_table_iter_init (&iter, priv->conns);
- while (g_hash_table_iter_next (&iter, &conn, &host))
+ while (g_hash_table_iter_next (&iter, &conn, &host)) {
conns = g_slist_prepend (conns, g_object_ref (conn));
+ g_hash_table_iter_remove (&iter);
+ drop_connection (session, host, conn);
+ }
g_mutex_unlock (&priv->conn_lock);
for (c = conns; c; c = c->next) {
@@ -1666,23 +2493,19 @@ prefetch_uri (SoupSession *session, SoupURI *uri,
}
/**
-* soup_session_prepare_for_uri:
-* @session: a #SoupSession
-* @uri: a #SoupURI which may be required
-*
-* Tells @session that @uri may be requested shortly, and so the
-* session can try to prepare (resolving the domain name, obtaining
-* proxy address, etc.) in order to work more quickly once the URI is
-* actually requested.
-*
-* This method acts asynchronously, in @session's
-* #SoupSession:async_context. If you are using #SoupSessionSync and do
-* not have a main loop running, then you can't use this method.
-*
-* Since: 2.30
-*
-* Deprecated: 2.38: use soup_session_prefetch_dns() instead
-**/
+ * soup_session_prepare_for_uri:
+ * @session: a #SoupSession
+ * @uri: a #SoupURI which may be required
+ *
+ * Tells @session that @uri may be requested shortly, and so the
+ * session can try to prepare (resolving the domain name, obtaining
+ * proxy address, etc.) in order to work more quickly once the URI is
+ * actually requested.
+ *
+ * Since: 2.30
+ *
+ * Deprecated: 2.38: use soup_session_prefetch_dns() instead
+ **/
void
soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
{
@@ -1713,10 +2536,6 @@ soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
* resolution. @callback will still be invoked in this case, with a
* status of %SOUP_STATUS_CANCELLED.
*
-* This method acts asynchronously, in @session's
-* #SoupSession:async_context. If you are using #SoupSessionSync and do
-* not have a main loop running, then you can't use this method.
-*
* Since: 2.38
**/
void
@@ -1748,6 +2567,9 @@ soup_session_prefetch_dns (SoupSession *session, const char *hostname,
* feature to the session at construct time by using the
* %SOUP_SESSION_ADD_FEATURE property.
*
+ * Note that a #SoupContentDecoder is added to the session by default
+ * (unless you are using one of the deprecated session subclasses).
+ *
* Since: 2.24
**/
void
@@ -1759,6 +2581,15 @@ soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
g_return_if_fail (SOUP_IS_SESSION_FEATURE (feature));
priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
+ set_proxy_resolver (session, NULL,
+ SOUP_PROXY_URI_RESOLVER (feature),
+ NULL);
+ }
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+
priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
g_hash_table_remove_all (priv->features_cache);
soup_session_feature_attach (feature, session);
@@ -1774,29 +2605,45 @@ soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
* adds it to @session as with soup_session_add_feature(). You can use
* this when you don't need to customize the new feature in any way.
*
- * If @feature_type is not a #SoupSessionFeature type, this gives
- * each existing feature on @session the chance to accept @feature_type
- * as a "subfeature". This can be used to add new #SoupAuth types,
- * for instance.
+ * If @feature_type is not a #SoupSessionFeature type, this gives each
+ * existing feature on @session the chance to accept @feature_type as
+ * a "subfeature". This can be used to add new #SoupAuth or
+ * #SoupRequest types, for instance.
*
* You can also add a feature to the session at construct time by
* using the %SOUP_SESSION_ADD_FEATURE_BY_TYPE property.
*
+ * Note that a #SoupContentDecoder is added to the session by default
+ * (unless you are using one of the deprecated session subclasses).
+ *
* Since: 2.24
**/
void
soup_session_add_feature_by_type (SoupSession *session, GType feature_type)
{
+ SoupSessionPrivate *priv;
+
g_return_if_fail (SOUP_IS_SESSION (session));
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
SoupSessionFeature *feature;
feature = g_object_new (feature_type, NULL);
soup_session_add_feature (session, feature);
g_object_unref (feature);
+ } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
+ SoupRequestClass *request_class;
+ int i;
+
+ request_class = g_type_class_ref (feature_type);
+ for (i = 0; request_class->schemes[i]; i++) {
+ g_hash_table_insert (priv->request_types,
+ (char *)request_class->schemes[i],
+ GSIZE_TO_POINTER (feature_type));
+ }
} else {
- SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
GSList *f;
for (f = priv->features; f; f = f->next) {
@@ -1828,6 +2675,15 @@ soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
priv->features = g_slist_remove (priv->features, feature);
g_hash_table_remove_all (priv->features_cache);
soup_session_feature_detach (feature, session);
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ if (SOUP_IS_PROXY_URI_RESOLVER (feature)) {
+ if (SOUP_IS_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver) &&
+ SOUP_PROXY_RESOLVER_WRAPPER (priv->proxy_resolver)->soup_resolver == SOUP_PROXY_URI_RESOLVER (feature))
+ g_clear_object (&priv->proxy_resolver);
+ }
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+
g_object_unref (feature);
}
}
@@ -1862,6 +2718,21 @@ soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
goto restart;
}
}
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ if (g_type_is_a (feature_type, SOUP_TYPE_PROXY_URI_RESOLVER))
+ priv->proxy_use_default = FALSE;
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
+ SoupRequestClass *request_class;
+ int i;
+
+ request_class = g_type_class_peek (feature_type);
+ if (!request_class)
+ return;
+ for (i = 0; request_class->schemes[i]; i++) {
+ g_hash_table_remove (priv->request_types,
+ request_class->schemes[i]);
+ }
} else {
for (f = priv->features; f; f = f->next) {
if (soup_session_feature_remove_feature (f->data, feature_type))
@@ -1872,6 +2743,49 @@ soup_session_remove_feature_by_type (SoupSession *session, GType feature_type)
}
/**
+ * soup_session_has_feature:
+ * @session: a #SoupSession
+ * @feature_type: the #GType of the class of features to check for
+ *
+ * Tests if @session has at a feature of type @feature_type (which can
+ * be the type of either a #SoupSessionFeature, or else a subtype of
+ * some class managed by another feature, such as #SoupAuth or
+ * #SoupRequest).
+ *
+ * Return value: %TRUE or %FALSE
+ *
+ * Since: 2.42
+ **/
+gboolean
+soup_session_has_feature (SoupSession *session,
+ GType feature_type)
+{
+ SoupSessionPrivate *priv;
+ GSList *f;
+
+ g_return_val_if_fail (SOUP_IS_SESSION (session), FALSE);
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ if (g_type_is_a (feature_type, SOUP_TYPE_SESSION_FEATURE)) {
+ for (f = priv->features; f; f = f->next) {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (f->data, feature_type))
+ return TRUE;
+ }
+ } else if (g_type_is_a (feature_type, SOUP_TYPE_REQUEST)) {
+ return g_hash_table_lookup (priv->request_types,
+ GSIZE_TO_POINTER (feature_type)) != NULL;
+ } else {
+ for (f = priv->features; f; f = f->next) {
+ if (soup_session_feature_has_feature (f->data, feature_type))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
* soup_session_get_features:
* @session: a #SoupSession
* @feature_type: the #GType of the class of features to get
@@ -1982,12 +2896,15 @@ soup_session_class_init (SoupSessionClass *session_class)
g_type_class_add_private (session_class, sizeof (SoupSessionPrivate));
/* virtual method definition */
+ session_class->queue_message = soup_session_real_queue_message;
+ session_class->send_message = soup_session_real_send_message;
session_class->requeue_message = soup_session_real_requeue_message;
session_class->cancel_message = soup_session_real_cancel_message;
- session_class->auth_required = soup_session_real_auth_required;
session_class->flush_queue = soup_session_real_flush_queue;
+ session_class->kick = soup_session_real_kick_queue;
/* virtual method override */
+ object_class->constructor = soup_session_constructor;
object_class->dispose = soup_session_dispose;
object_class->finalize = soup_session_finalize;
object_class->set_property = soup_session_set_property;
@@ -2036,7 +2953,7 @@ soup_session_class_init (SoupSessionClass *session_class)
* #SoupMessage::finished (and all of the other #SoupMessage
* signals) may be invoked multiple times for a given message.
*
- * Since: 2.4.1
+ * Since: 2.24
**/
signals[REQUEST_QUEUED] =
g_signal_new ("request-queued",
@@ -2044,7 +2961,7 @@ soup_session_class_init (SoupSessionClass *session_class)
G_SIGNAL_RUN_FIRST,
0, /* FIXME? */
NULL, NULL,
- _soup_marshal_NONE__OBJECT,
+ NULL,
G_TYPE_NONE, 1,
SOUP_TYPE_MESSAGE);
@@ -2064,7 +2981,7 @@ soup_session_class_init (SoupSessionClass *session_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupSessionClass, request_started),
NULL, NULL,
- _soup_marshal_NONE__OBJECT_OBJECT,
+ NULL,
G_TYPE_NONE, 2,
SOUP_TYPE_MESSAGE,
SOUP_TYPE_SOCKET);
@@ -2079,7 +2996,7 @@ soup_session_class_init (SoupSessionClass *session_class)
* #SoupSession::request_queued for a detailed description of the
* message lifecycle within a session.
*
- * Since: 2.4.1
+ * Since: 2.24
**/
signals[REQUEST_UNQUEUED] =
g_signal_new ("request-unqueued",
@@ -2087,7 +3004,7 @@ soup_session_class_init (SoupSessionClass *session_class)
G_SIGNAL_RUN_FIRST,
0, /* FIXME? */
NULL, NULL,
- _soup_marshal_NONE__OBJECT,
+ NULL,
G_TYPE_NONE, 1,
SOUP_TYPE_MESSAGE);
@@ -2120,7 +3037,7 @@ soup_session_class_init (SoupSessionClass *session_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupSessionClass, authenticate),
NULL, NULL,
- _soup_marshal_NONE__OBJECT_OBJECT_BOOLEAN,
+ NULL,
G_TYPE_NONE, 3,
SOUP_TYPE_MESSAGE,
SOUP_TYPE_AUTH,
@@ -2143,7 +3060,7 @@ soup_session_class_init (SoupSessionClass *session_class)
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
- _soup_marshal_NONE__OBJECT,
+ NULL,
G_TYPE_NONE, 1,
/* SoupConnection is private, so we can't use
* SOUP_TYPE_CONNECTION here.
@@ -2167,7 +3084,7 @@ soup_session_class_init (SoupSessionClass *session_class)
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
- _soup_marshal_NONE__OBJECT,
+ NULL,
G_TYPE_NONE, 1,
/* SoupConnection is private, so we can't use
* SOUP_TYPE_CONNECTION here.
@@ -2177,10 +3094,30 @@ soup_session_class_init (SoupSessionClass *session_class)
/* properties */
/**
+ * SoupSession:proxy-uri:
+ *
+ * A proxy to use for all http and https requests in this
+ * session. Setting this will clear the
+ * #SoupSession:proxy-resolver property, and remove any
+ * <type>SoupProxyURIResolver</type> features that have been
+ * added to the session. Setting this property will also
+ * cancel all currently pending messages.
+ *
+ * Note that #SoupSession will normally handle looking up the
+ * user's proxy settings for you; you should only use
+ * #SoupSession:proxy-uri if you need to override the user's
+ * normal proxy settings.
+ *
+ * Also note that this proxy will be used for
+ * <emphasis>all</emphasis> requests; even requests to
+ * <literal>localhost</literal>. If you need more control over
+ * proxies, you can create a #GSimpleProxyResolver and set the
+ * #SoupSession:proxy-resolver property.
+ */
+ /**
* SOUP_SESSION_PROXY_URI:
*
- * Alias for the #SoupSession:proxy-uri property. (The HTTP
- * proxy to use for this session.)
+ * Alias for the #SoupSession:proxy-uri property, qv.
**/
g_object_class_install_property (
object_class, PROP_PROXY_URI,
@@ -2190,10 +3127,37 @@ soup_session_class_init (SoupSessionClass *session_class)
SOUP_TYPE_URI,
G_PARAM_READWRITE));
/**
+ * SoupSession:proxy-resolver:
+ *
+ * A #GProxyResolver to use with this session. Setting this
+ * will clear the #SoupSession:proxy-uri property, and remove
+ * any <type>SoupProxyURIResolver</type> features that have
+ * been added to the session.
+ *
+ * By default, in a plain #SoupSession, this is set to the
+ * default #GProxyResolver, but you can set it to %NULL if you
+ * don't want to use proxies, or set it to your own
+ * #GProxyResolver if you want to control what proxies get
+ * used.
+ *
+ * Since: 2.42
+ */
+ /**
+ * SOUP_SESSION_PROXY_RESOLVER:
+ *
+ * Alias for the #SoupSession:proxy-resolver property, qv.
+ **/
+ g_object_class_install_property (
+ object_class, PROP_PROXY_RESOLVER,
+ g_param_spec_object (SOUP_SESSION_PROXY_RESOLVER,
+ "Proxy Resolver",
+ "The GProxyResolver to use for this session",
+ G_TYPE_PROXY_RESOLVER,
+ G_PARAM_READWRITE));
+ /**
* SOUP_SESSION_MAX_CONNS:
*
- * Alias for the #SoupSession:max-conns property. (The maximum
- * number of connections that the session can open at once.)
+ * Alias for the #SoupSession:max-conns property, qv.
**/
g_object_class_install_property (
object_class, PROP_MAX_CONNS,
@@ -2207,9 +3171,7 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SOUP_SESSION_MAX_CONNS_PER_HOST:
*
- * Alias for the #SoupSession:max-conns-per-host property.
- * (The maximum number of connections that the session can
- * open at once to a given host.)
+ * Alias for the #SoupSession:max-conns-per-host property, qv.
**/
g_object_class_install_property (
object_class, PROP_MAX_CONNS_PER_HOST,
@@ -2223,24 +3185,35 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SoupSession:idle-timeout:
*
- * Connection lifetime when idle
+ * Connection lifetime (in seconds) when idle. Any connection
+ * left idle longer than this will be closed.
*
- * Since: 2.4.1
+ * Although you can change this property at any time, it will
+ * only affect newly-created connections, not currently-open
+ * ones. You can call soup_session_abort() after setting this
+ * if you want to ensure that all future connections will have
+ * this timeout value.
+ *
+ * Note that the default value of 60 seconds only applies to
+ * plain #SoupSessions. If you are using #SoupSessionAsync or
+ * #SoupSessionSync, the default value is 0 (meaning idle
+ * connections will never time out).
+ *
+ * Since: 2.24
**/
/**
* SOUP_SESSION_IDLE_TIMEOUT:
*
- * Alias for the #SoupSession:idle-timeout property. (The idle
- * connection lifetime.)
+ * Alias for the #SoupSession:idle-timeout property, qv.
*
- * Since: 2.4.1
+ * Since: 2.24
**/
g_object_class_install_property (
object_class, PROP_IDLE_TIMEOUT,
g_param_spec_uint (SOUP_SESSION_IDLE_TIMEOUT,
"Idle Timeout",
"Connection lifetime when idle",
- 0, G_MAXUINT, 0,
+ 0, G_MAXUINT, 60,
G_PARAM_READWRITE));
/**
* SoupSession:use-ntlm:
@@ -2253,8 +3226,7 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SOUP_SESSION_USE_NTLM:
*
- * Alias for the #SoupSession:use-ntlm property. (Whether or
- * not to use NTLM authentication.)
+ * Alias for the #SoupSession:use-ntlm property, qv.
**/
g_object_class_install_property (
object_class, PROP_USE_NTLM,
@@ -2262,23 +3234,25 @@ soup_session_class_init (SoupSessionClass *session_class)
"Use NTLM",
"Whether or not to use NTLM authentication",
FALSE,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_DEPRECATED));
/**
* SoupSession:ssl-ca-file:
*
* File containing SSL CA certificates.
*
- * Deprecated: use #SoupSession:ssl-use-system-ca-file or
- * #SoupSession:tls-database instead
+ * If the specified file does not exist or cannot be read,
+ * then libsoup will print a warning, and then behave as
+ * though it had read in a empty CA file, meaning that all SSL
+ * certificates will be considered invalid.
+ *
+ * Deprecated: use #SoupSession:ssl-use-system-ca-file, or
+ * else #SoupSession:tls-database with a #GTlsFileDatabase
+ * (which allows you to do explicit error handling).
**/
/**
* SOUP_SESSION_SSL_CA_FILE:
*
- * Alias for the #SoupSession:ssl-ca-file property. (File
- * containing SSL CA certificates.).
- *
- * Deprecated: use #SoupSession:ssl-use-system-ca-file or
- * #SoupSession:tls-database instead
+ * Alias for the #SoupSession:ssl-ca-file property, qv.
**/
g_object_class_install_property (
object_class, PROP_SSL_CA_FILE,
@@ -2286,9 +3260,9 @@ soup_session_class_init (SoupSessionClass *session_class)
"SSL CA file",
"File containing SSL CA certificates",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_DEPRECATED));
/**
- * SOUP_SESSION_USE_SYSTEM_CA_FILE:
+ * SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE:
*
* Alias for the #SoupSession:ssl-use-system-ca-file property,
* qv.
@@ -2310,6 +3284,11 @@ soup_session_class_init (SoupSessionClass *session_class)
* See #SoupSession:ssl-strict for more information on how
* https certificate validation is handled.
*
+ * Note that the default value of %TRUE only applies to plain
+ * #SoupSessions. If you are using #SoupSessionAsync or
+ * #SoupSessionSync, the default value is %FALSE, for backward
+ * compatibility.
+ *
* Since: 2.38
**/
g_object_class_install_property (
@@ -2317,7 +3296,7 @@ soup_session_class_init (SoupSessionClass *session_class)
g_param_spec_boolean (SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
"Use system CA file",
"Use the system certificate database",
- FALSE,
+ TRUE,
G_PARAM_READWRITE));
/**
* SOUP_SESSION_TLS_DATABASE:
@@ -2340,6 +3319,12 @@ soup_session_class_init (SoupSessionClass *session_class)
* See #SoupSession:ssl-strict for more information on how
* https certificate validation is handled.
*
+ * If you are using a plain #SoupSession then
+ * #SoupSession:ssl-use-system-ca-file will be %TRUE by
+ * default, and so this property will be a copy of the system
+ * CA database. If you are using #SoupSessionAsync or
+ * #SoupSessionSync, this property will be %NULL by default.
+ *
* Since: 2.38
**/
g_object_class_install_property (
@@ -2359,8 +3344,8 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SoupSession:ssl-strict:
*
- * Normally, if #SoupSession:tlsdb is set (including if it was
- * set via #SoupSession:ssl-use-system-ca-file or
+ * Normally, if #SoupSession:tls-database is set (including if
+ * it was set via #SoupSession:ssl-use-system-ca-file or
* #SoupSession:ssl-ca-file), then libsoup will reject any
* certificate that is invalid (ie, expired) or that is not
* signed by one of the given CA certificates, and the
@@ -2374,9 +3359,13 @@ soup_session_class_init (SoupSessionClass *session_class)
* accept invalid certificates after giving some sort of
* warning.)
*
- * If the session has no CA file or TLS database, then all
- * certificates are always accepted, and this property has no
- * effect.
+ * For a plain #SoupSession, if the session has no CA file or
+ * TLS database, and this property is %TRUE, then all
+ * certificates will be rejected. However, beware that the
+ * deprecated #SoupSession subclasses (#SoupSessionAsync and
+ * #SoupSessionSync) have the opposite behavior: if there is
+ * no CA file or TLS database, then all certificates are always
+ * accepted, and this property has no effect.
*
* Since: 2.30
*/
@@ -2388,10 +3377,26 @@ soup_session_class_init (SoupSessionClass *session_class)
TRUE,
G_PARAM_READWRITE));
/**
+ * SoupSession:async-context:
+ *
+ * The #GMainContext that miscellaneous session-related
+ * asynchronous callbacks are invoked on. (Eg, setting
+ * #SoupSession:idle-timeout will add a timeout source on this
+ * context.)
+ *
+ * For a plain #SoupSession, this property is always set to
+ * the #GMainContext that is the thread-default at the time
+ * the session was created, and cannot be overridden. For the
+ * deprecated #SoupSession subclasses, the default value is
+ * %NULL, meaning to use the global default #GMainContext.
+ *
+ * If #SoupSession:use-thread-context is %FALSE, this context
+ * will also be used for asynchronous HTTP I/O.
+ */
+ /**
* SOUP_SESSION_ASYNC_CONTEXT:
*
- * Alias for the #SoupSession:async-context property. (The
- * session's #GMainContext.)
+ * Alias for the #SoupSession:async-context property, qv.
*/
g_object_class_install_property (
object_class, PROP_ASYNC_CONTEXT,
@@ -2409,13 +3414,11 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SoupSession:use-thread-context:
*
- * If set, asynchronous operations in this session will run in
+ * If %TRUE (which it always is on a plain #SoupSession),
+ * asynchronous HTTP requests in this session will run in
* whatever the thread-default #GMainContext is at the time
- * they are started, rather than always occurring in a context
- * fixed at the session's construction time. "Bookkeeping"
- * tasks (like expiring idle connections) will happen in the
- * context that was thread-default at the time the session was
- * created.
+ * they are started, rather than always occurring in
+ * #SoupSession:async-context.
*
* Since: 2.38
*/
@@ -2427,10 +3430,31 @@ soup_session_class_init (SoupSessionClass *session_class)
FALSE,
G_PARAM_READWRITE));
/**
+ * SoupSession:timeout:
+ *
+ * The timeout (in seconds) for socket I/O operations
+ * (including connecting to a server, and waiting for a reply
+ * to an HTTP request).
+ *
+ * Although you can change this property at any time, it will
+ * only affect newly-created connections, not currently-open
+ * ones. You can call soup_session_abort() after setting this
+ * if you want to ensure that all future connections will have
+ * this timeout value.
+ *
+ * Note that the default value of 60 seconds only applies to
+ * plain #SoupSessions. If you are using #SoupSessionAsync or
+ * #SoupSessionSync, the default value is 0 (meaning socket I/O
+ * will not time out).
+ *
+ * Not to be confused with #SoupSession:idle-timeout (which is
+ * the length of time that idle persistent connections will be
+ * kept open).
+ */
+ /**
* SOUP_SESSION_TIMEOUT:
*
- * Alias for the #SoupSession:timeout property. (The timeout
- * in seconds for blocking socket I/O operations.)
+ * Alias for the #SoupSession:timeout property, qv.
**/
g_object_class_install_property (
object_class, PROP_TIMEOUT,
@@ -2544,8 +3568,7 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SOUP_SESSION_ADD_FEATURE: (skip)
*
- * Alias for the #SoupSession:add-feature property. (Shortcut
- * for calling soup_session_add_feature().
+ * Alias for the #SoupSession:add-feature property, qv.
*
* Since: 2.24
**/
@@ -2567,8 +3590,7 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SOUP_SESSION_ADD_FEATURE_BY_TYPE: (skip)
*
- * Alias for the #SoupSession:add-feature-by-type property.
- * (Shortcut for calling soup_session_add_feature_by_type().
+ * Alias for the #SoupSession:add-feature-by-type property, qv.
*
* Since: 2.24
**/
@@ -2577,7 +3599,7 @@ soup_session_class_init (SoupSessionClass *session_class)
g_param_spec_gtype (SOUP_SESSION_ADD_FEATURE_BY_TYPE,
"Add Feature By Type",
"Add a feature object of the given type to the session",
- SOUP_TYPE_SESSION_FEATURE,
+ G_TYPE_OBJECT,
G_PARAM_READWRITE));
/**
* SoupSession:remove-feature-by-type: (skip)
@@ -2590,9 +3612,8 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SOUP_SESSION_REMOVE_FEATURE_BY_TYPE: (skip)
*
- * Alias for the #SoupSession:remove-feature-by-type
- * property. (Shortcut for calling
- * soup_session_remove_feature_by_type().
+ * Alias for the #SoupSession:remove-feature-by-type property,
+ * qv.
*
* Since: 2.24
**/
@@ -2601,7 +3622,7 @@ soup_session_class_init (SoupSessionClass *session_class)
g_param_spec_gtype (SOUP_SESSION_REMOVE_FEATURE_BY_TYPE,
"Remove Feature By Type",
"Remove features of the given type from the session",
- SOUP_TYPE_SESSION_FEATURE,
+ G_TYPE_OBJECT,
G_PARAM_READWRITE));
/**
* SoupSession:http-aliases:
@@ -2611,14 +3632,14 @@ soup_session_class_init (SoupSessionClass *session_class)
* <literal>"dav"</literal>, than a URI of
* <literal>dav://example.com/path</literal> would be treated
* identically to <literal>http://example.com/path</literal>.
- * If the value is %NULL, then only "http" is recognized as
- * meaning "http".
*
- * For backward-compatibility reasons, the default value for
- * this property is an array containing the single element
- * <literal>"*"</literal>, a special value which means that
- * any scheme except "https" is considered to be an alias for
- * "http".
+ * In a plain #SoupSession, the default value is %NULL,
+ * meaning that only "http" is recognized as meaning "http".
+ * In #SoupSessionAsync and #SoupSessionSync, for backward
+ * compatibility, the default value is an array containing the
+ * single element <literal>"*"</literal>, a special value
+ * which means that any scheme except "https" is considered to
+ * be an alias for "http".
*
* See also #SoupSession:https-aliases.
*
@@ -2627,8 +3648,7 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SOUP_SESSION_HTTP_ALIASES:
*
- * Alias for the #SoupSession:http-aliases property. (URI
- * schemes that will be considered aliases for "http".)
+ * Alias for the #SoupSession:http-aliases property, qv.
*
* Since: 2.38
*/
@@ -2654,8 +3674,7 @@ soup_session_class_init (SoupSessionClass *session_class)
/**
* SOUP_SESSION_HTTPS_ALIASES:
*
- * Alias for the #SoupSession:https-aliases property. (URI
- * schemes that will be considered aliases for "https".)
+ * Alias for the #SoupSession:https-aliases property, qv.
*
* Since: 2.38
**/
@@ -2666,4 +3685,874 @@ soup_session_class_init (SoupSessionClass *session_class)
"URI schemes that are considered aliases for 'https'",
G_TYPE_STRV,
G_PARAM_READWRITE));
+
+ /**
+ * SOUP_SESSION_LOCAL_ADDRESS:
+ *
+ * Alias for the #SoupSession:local-address property, qv.
+ *
+ * Since: 2.42
+ **/
+ /**
+ * SoupSession:local-address:
+ *
+ * Sets the #SoupAddress to use for the client side of
+ * the connection.
+ *
+ * Use this property if you want for instance to bind the
+ * local socket to a specific IP address.
+ *
+ * Since: 2.42
+ **/
+ g_object_class_install_property (
+ object_class, PROP_LOCAL_ADDRESS,
+ g_param_spec_object (SOUP_SESSION_LOCAL_ADDRESS,
+ "Local address",
+ "Address of local end of socket",
+ SOUP_TYPE_ADDRESS,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+
+static gboolean
+expected_to_be_requeued (SoupSession *session, SoupMessage *msg)
+{
+ if (msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
+ msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
+ SoupSessionFeature *feature =
+ soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
+ return !feature || !soup_message_disables_feature (msg, feature);
+ }
+
+ if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT))
+ return soup_session_would_redirect (session, msg);
+
+ return FALSE;
+}
+
+/* send_request_async */
+
+static void
+async_send_request_return_result (SoupMessageQueueItem *item,
+ gpointer stream, GError *error)
+{
+ GTask *task;
+
+ g_return_if_fail (item->task != NULL);
+
+ g_signal_handlers_disconnect_matched (item->msg, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, item);
+
+ task = item->task;
+ item->task = NULL;
+
+ if (item->io_source) {
+ g_source_destroy (item->io_source);
+ g_clear_pointer (&item->io_source, g_source_unref);
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else if (item->error) {
+ if (stream)
+ g_object_unref (stream);
+ g_task_return_error (task, g_error_copy (item->error));
+ } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
+ if (stream)
+ g_object_unref (stream);
+ g_task_return_new_error (task, SOUP_HTTP_ERROR,
+ item->msg->status_code,
+ "%s",
+ item->msg->reason_phrase);
+ } else
+ g_task_return_pointer (task, stream, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+async_send_request_restarted (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ /* We won't be needing this, then. */
+ g_object_set_data (G_OBJECT (item->msg), "SoupSession:ostream", NULL);
+ item->io_started = FALSE;
+}
+
+static void
+async_send_request_finished (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ GMemoryOutputStream *mostream;
+ GInputStream *istream = NULL;
+ GError *error = NULL;
+
+ if (!item->task) {
+ /* Something else already took care of it. */
+ return;
+ }
+
+ mostream = g_object_get_data (G_OBJECT (item->task), "SoupSession:ostream");
+ if (mostream) {
+ gpointer data;
+ gssize size;
+
+ /* We thought it would be requeued, but it wasn't, so
+ * return the original body.
+ */
+ size = g_memory_output_stream_get_data_size (mostream);
+ data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
+ istream = g_memory_input_stream_new_from_data (data, size, g_free);
+ } else if (item->io_started) {
+ /* The message finished before becoming readable. This
+ * will happen, eg, if it's cancelled from got-headers.
+ * Do nothing; the op will complete via read_ready_cb()
+ * after we return;
+ */
+ return;
+ } else {
+ /* The message finished before even being started;
+ * probably a tunnel connect failure.
+ */
+ istream = g_memory_input_stream_new ();
+ }
+
+ async_send_request_return_result (item, istream, error);
+}
+
+static void
+send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ GInputStream *istream = g_object_get_data (source, "istream");
+ GError *error = NULL;
+
+ /* It should be safe to call the sync close() method here since
+ * the message body has already been written.
+ */
+ g_input_stream_close (istream, NULL, NULL);
+ g_object_unref (istream);
+
+ /* If the message was cancelled, it will be completed via other means */
+ if (g_cancellable_is_cancelled (item->cancellable) ||
+ !item->task) {
+ soup_message_queue_item_unref (item);
+ return;
+ }
+
+ if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
+ result, &error) == -1) {
+ async_send_request_return_result (item, NULL, error);
+ soup_message_queue_item_unref (item);
+ return;
+ }
+
+ /* Otherwise either restarted or finished will eventually be called. */
+ soup_session_kick_queue (item->session);
+ soup_message_queue_item_unref (item);
+}
+
+static void
+send_async_maybe_complete (SoupMessageQueueItem *item,
+ GInputStream *stream)
+{
+ if (expected_to_be_requeued (item->session, item->msg)) {
+ GOutputStream *ostream;
+
+ /* Gather the current message body... */
+ ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+ g_object_set_data_full (G_OBJECT (item->task), "SoupSession:ostream",
+ ostream, g_object_unref);
+
+ g_object_set_data (G_OBJECT (ostream), "istream", stream);
+
+ /* Give the splice op its own ref on item */
+ soup_message_queue_item_ref (item);
+ /* We don't use CLOSE_SOURCE because we need to control when the
+ * side effects of closing the SoupClientInputStream happen.
+ */
+ g_output_stream_splice_async (ostream, stream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT,
+ item->cancellable,
+ send_async_spliced, item);
+ return;
+ }
+
+ async_send_request_return_result (item, stream, NULL);
+}
+
+static void try_run_until_read (SoupMessageQueueItem *item);
+
+static gboolean
+read_ready_cb (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ g_clear_pointer (&item->io_source, g_source_unref);
+ try_run_until_read (item);
+ return FALSE;
+}
+
+static void
+try_run_until_read (SoupMessageQueueItem *item)
+{
+ GError *error = NULL;
+ GInputStream *stream = NULL;
+
+ if (soup_message_io_run_until_read (item->msg, FALSE, item->cancellable, &error))
+ stream = soup_message_io_get_response_istream (item->msg, &error);
+ if (stream) {
+ send_async_maybe_complete (item, stream);
+ return;
+ }
+
+ if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
+ item->state = SOUP_MESSAGE_RESTARTING;
+ soup_message_io_finished (item->msg);
+ g_error_free (error);
+ return;
+ }
+
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ if (item->state != SOUP_MESSAGE_FINISHED) {
+ if (soup_message_io_in_progress (item->msg))
+ soup_message_io_finished (item->msg);
+ item->state = SOUP_MESSAGE_FINISHING;
+ soup_session_process_queue_item (item->session, item, NULL, FALSE);
+ }
+ async_send_request_return_result (item, NULL, error);
+ return;
+ }
+
+ g_clear_error (&error);
+ item->io_source = soup_message_io_get_source (item->msg, item->cancellable,
+ read_ready_cb, item);
+ g_source_attach (item->io_source, soup_session_get_async_context (item->session));
+}
+
+static void
+async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
+{
+ item->io_started = TRUE;
+ try_run_until_read (item);
+}
+
+static void
+async_return_from_cache (SoupMessageQueueItem *item,
+ GInputStream *stream)
+{
+ const char *content_type;
+ GHashTable *params = NULL;
+
+ soup_message_got_headers (item->msg);
+
+ content_type = soup_message_headers_get_content_type (item->msg->response_headers, &params);
+ if (content_type) {
+ soup_message_content_sniffed (item->msg, content_type, params);
+ g_hash_table_unref (params);
+ }
+
+ item->state = SOUP_MESSAGE_FINISHING;
+ async_send_request_return_result (item, g_object_ref (stream), NULL);
+}
+
+typedef struct {
+ SoupCache *cache;
+ SoupMessage *conditional_msg;
+} AsyncCacheCancelData;
+
+
+static void
+free_async_cache_cancel_data (AsyncCacheCancelData *data)
+{
+ g_object_unref (data->conditional_msg);
+ g_object_unref (data->cache);
+ g_slice_free (AsyncCacheCancelData, data);
+}
+
+static void
+cancel_cache_response (SoupMessageQueueItem *item)
+{
+ item->paused = FALSE;
+ item->state = SOUP_MESSAGE_FINISHING;
+ soup_message_set_status (item->msg, SOUP_STATUS_CANCELLED);
+ soup_session_kick_queue (item->session);
+}
+
+static void
+conditional_request_cancelled_cb (GCancellable *cancellable, AsyncCacheCancelData *data)
+{
+ soup_cache_cancel_conditional_request (data->cache, data->conditional_msg);
+}
+
+static void
+conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+ GInputStream *stream;
+ SoupCache *cache;
+
+ if (g_cancellable_is_cancelled (item->cancellable)) {
+ cancel_cache_response (item);
+ return;
+ } else {
+ gulong handler_id = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (msg), "SoupSession:handler-id"));
+ g_cancellable_disconnect (item->cancellable, handler_id);
+ }
+
+ cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+ soup_cache_update_from_conditional_request (cache, msg);
+
+ if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
+ stream = soup_cache_send_response (cache, item->msg);
+ if (stream) {
+ async_return_from_cache (item, stream);
+ g_object_unref (stream);
+ return;
+ }
+ }
+
+ /* The resource was modified or the server returned a 200
+ * OK. Either way we reload it. FIXME.
+ */
+ item->state = SOUP_MESSAGE_STARTING;
+ soup_session_kick_queue (session);
+}
+
+static gboolean
+idle_return_from_cache_cb (gpointer data)
+{
+ GTask *task = data;
+ SoupMessageQueueItem *item = g_task_get_task_data (task);
+ GInputStream *istream;
+
+ if (item->state == SOUP_MESSAGE_FINISHED) {
+ /* The original request was cancelled using
+ * soup_session_cancel_message () so it has been
+ * already handled by the cancellation code path.
+ */
+ return FALSE;
+ } else if (g_cancellable_is_cancelled (item->cancellable)) {
+ /* Cancel original msg after g_cancellable_cancel(). */
+ cancel_cache_response (item);
+ return FALSE;
+ }
+
+ istream = g_object_get_data (G_OBJECT (task), "SoupSession:istream");
+ async_return_from_cache (item, istream);
+
+ return FALSE;
+}
+
+
+static gboolean
+async_respond_from_cache (SoupSession *session,
+ SoupMessageQueueItem *item)
+{
+ SoupCache *cache;
+ SoupCacheResponse response;
+
+ cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+ if (!cache)
+ return FALSE;
+
+ response = soup_cache_has_response (cache, item->msg);
+ if (response == SOUP_CACHE_RESPONSE_FRESH) {
+ GInputStream *stream;
+ GSource *source;
+
+ stream = soup_cache_send_response (cache, item->msg);
+ if (!stream) {
+ /* Cached file was deleted? */
+ return FALSE;
+ }
+ g_object_set_data_full (G_OBJECT (item->task), "SoupSession:istream",
+ stream, g_object_unref);
+
+ source = g_timeout_source_new (0);
+ g_task_attach_source (item->task, source,
+ (GSourceFunc) idle_return_from_cache_cb);
+ g_source_unref (source);
+ return TRUE;
+ } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
+ SoupMessage *conditional_msg;
+ AsyncCacheCancelData *data;
+ gulong handler_id;
+
+ conditional_msg = soup_cache_generate_conditional_request (cache, item->msg);
+ if (!conditional_msg)
+ return FALSE;
+
+ /* Detect any quick cancellation before the cache is able to return data. */
+ data = g_slice_new0 (AsyncCacheCancelData);
+ data->cache = g_object_ref (cache);
+ data->conditional_msg = g_object_ref (conditional_msg);
+ handler_id = g_cancellable_connect (item->cancellable, G_CALLBACK (conditional_request_cancelled_cb),
+ data, (GDestroyNotify) free_async_cache_cancel_data);
+
+ g_object_set_data (G_OBJECT (conditional_msg), "SoupSession:handler-id",
+ GSIZE_TO_POINTER (handler_id));
+ soup_session_queue_message (session, conditional_msg,
+ conditional_get_ready_cb,
+ item);
+
+
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+/**
+ * soup_session_send_async:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage
+ * @cancellable: a #GCancellable
+ * @callback: the callback to invoke
+ * @user_data: data for @callback
+ *
+ * Asynchronously sends @msg and waits for the beginning of a
+ * response. When @callback is called, then either @msg has been sent,
+ * and its response headers received, or else an error has occurred.
+ * Call soup_session_send_finish() to get a #GInputStream for reading
+ * the response body.
+ *
+ * See soup_session_send() for more details on the general semantics.
+ *
+ * Contrast this method with soup_session_queue_message(), which also
+ * asynchronously sends a #SoupMessage, but doesn't invoke its
+ * callback until the response has been completely read.
+ *
+ * (Note that this method cannot be called on the deprecated
+ * #SoupSessionSync subclass, and can only be called on
+ * #SoupSessionAsync if you have set the
+ * #SoupSession:use-thread-context property.)
+ *
+ * Since: 2.42
+ */
+void
+soup_session_send_async (SoupSession *session,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupMessageQueueItem *item;
+ gboolean use_thread_context;
+
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
+
+ g_object_get (G_OBJECT (session),
+ SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
+ NULL);
+ g_return_if_fail (use_thread_context);
+
+ item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
+ NULL, NULL);
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (async_send_request_restarted), item);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (async_send_request_finished), item);
+
+ if (cancellable) {
+ g_object_unref (item->cancellable);
+ item->cancellable = g_object_ref (cancellable);
+ }
+
+ item->new_api = TRUE;
+ item->task = g_task_new (session, item->cancellable, callback, user_data);
+ g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
+
+ /* Do not check for cancellations as we do not want to
+ * overwrite custom error messages set during cancellations
+ * (for example SOUP_HTTP_ERROR is set for cancelled messages
+ * in async_send_request_return_result() (status_code==1
+ * means CANCEL and is considered a TRANSPORT_ERROR)).
+ */
+ g_task_set_check_cancellable (item->task, FALSE);
+
+ if (async_respond_from_cache (session, item))
+ item->state = SOUP_MESSAGE_CACHED;
+ else
+ soup_session_kick_queue (session);
+}
+
+/**
+ * soup_session_send_finish:
+ * @session: a #SoupSession
+ * @result: the #GAsyncResult passed to your callback
+ * @error: return location for a #GError, or %NULL
+ *
+ * Gets the response to a soup_session_send_async() call and (if
+ * successful), returns a #GInputStream that can be used to read the
+ * response body.
+ *
+ * Return value: (transfer full): a #GInputStream for reading the
+ * response body, or %NULL on error.
+ *
+ * Since: 2.42
+ */
+GInputStream *
+soup_session_send_finish (SoupSession *session,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task;
+
+ g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+ g_return_val_if_fail (!SOUP_IS_SESSION_SYNC (session), NULL);
+ g_return_val_if_fail (g_task_is_valid (result, session), NULL);
+
+ task = G_TASK (result);
+ if (g_task_had_error (task)) {
+ SoupMessageQueueItem *item = g_task_get_task_data (task);
+
+ if (soup_message_io_in_progress (item->msg))
+ soup_message_io_finished (item->msg);
+ else if (item->state != SOUP_MESSAGE_FINISHED)
+ item->state = SOUP_MESSAGE_FINISHING;
+
+ if (item->state != SOUP_MESSAGE_FINISHED)
+ soup_session_process_queue_item (session, item, NULL, FALSE);
+ }
+
+ return g_task_propagate_pointer (task, error);
+}
+
+/**
+ * soup_session_send:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage
+ * @cancellable: a #GCancellable
+ * @error: return location for a #GError, or %NULL
+ *
+ * Synchronously sends @msg and waits for the beginning of a response.
+ * On success, a #GInputStream will be returned which you can use to
+ * read the response body. ("Success" here means only that an HTTP
+ * response was received and understood; it does not necessarily mean
+ * that a 2xx class status code was received.)
+ *
+ * If non-%NULL, @cancellable can be used to cancel the request;
+ * soup_session_send() will return a %G_IO_ERROR_CANCELLED error. Note
+ * that with requests that have side effects (eg,
+ * <literal>POST</literal>, <literal>PUT</literal>,
+ * <literal>DELETE</literal>) it is possible that you might cancel the
+ * request after the server acts on it, but before it returns a
+ * response, leaving the remote resource in an unknown state.
+ *
+ * If @msg is requeued due to a redirect or authentication, the
+ * initial (3xx/401/407) response body will be suppressed, and
+ * soup_session_send() will only return once a final response has been
+ * received.
+ *
+ * Contrast this method with soup_session_send_message(), which also
+ * synchronously sends a #SoupMessage, but doesn't return until the
+ * response has been completely read.
+ *
+ * (Note that this method cannot be called on the deprecated
+ * #SoupSessionAsync subclass.)
+ *
+ * Return value: (transfer full): a #GInputStream for reading the
+ * response body, or %NULL on error.
+ *
+ * Since: 2.42
+ */
+GInputStream *
+soup_session_send (SoupSession *session,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupMessageQueueItem *item;
+ GInputStream *stream = NULL;
+ GOutputStream *ostream;
+ GMemoryOutputStream *mostream;
+ gssize size;
+ GError *my_error = NULL;
+
+ g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+ g_return_val_if_fail (!SOUP_IS_SESSION_ASYNC (session), NULL);
+
+ item = soup_session_append_queue_item (session, msg, FALSE, TRUE,
+ NULL, NULL);
+
+ item->new_api = TRUE;
+ if (cancellable) {
+ g_object_unref (item->cancellable);
+ item->cancellable = g_object_ref (cancellable);
+ }
+
+ while (!stream) {
+ /* Get a connection, etc */
+ soup_session_process_queue_item (session, item, NULL, TRUE);
+ if (item->state != SOUP_MESSAGE_RUNNING)
+ break;
+
+ /* Send request, read headers */
+ if (!soup_message_io_run_until_read (msg, TRUE, item->cancellable, &my_error)) {
+ if (g_error_matches (my_error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
+ item->state = SOUP_MESSAGE_RESTARTING;
+ soup_message_io_finished (item->msg);
+ g_clear_error (&my_error);
+ continue;
+ } else
+ break;
+ }
+
+ stream = soup_message_io_get_response_istream (msg, &my_error);
+ if (!stream)
+ break;
+
+ if (!expected_to_be_requeued (session, msg))
+ break;
+
+ /* Gather the current message body... */
+ ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+ if (g_output_stream_splice (ostream, stream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ item->cancellable, &my_error) == -1) {
+ g_object_unref (stream);
+ g_object_unref (ostream);
+ stream = NULL;
+ break;
+ }
+ g_object_unref (stream);
+ stream = NULL;
+
+ /* If the message was requeued, loop */
+ if (item->state == SOUP_MESSAGE_RESTARTING) {
+ g_object_unref (ostream);
+ continue;
+ }
+
+ /* Not requeued, so return the original body */
+ mostream = G_MEMORY_OUTPUT_STREAM (ostream);
+ size = g_memory_output_stream_get_data_size (mostream);
+ stream = g_memory_input_stream_new ();
+ if (size) {
+ g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream),
+ g_memory_output_stream_steal_data (mostream),
+ size, g_free);
+ }
+ g_object_unref (ostream);
+ }
+
+ if (my_error)
+ g_propagate_error (error, my_error);
+ else if (item->error) {
+ g_clear_object (&stream);
+ if (error)
+ *error = g_error_copy (item->error);
+ } else if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
+ g_clear_object (&stream);
+ g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code,
+ msg->reason_phrase);
+ } else if (!stream)
+ stream = g_memory_input_stream_new ();
+
+ if (!stream) {
+ if (soup_message_io_in_progress (msg))
+ soup_message_io_finished (msg);
+ else if (item->state != SOUP_MESSAGE_FINISHED)
+ item->state = SOUP_MESSAGE_FINISHING;
+
+ if (item->state != SOUP_MESSAGE_FINISHED)
+ soup_session_process_queue_item (session, item, NULL, TRUE);
+ }
+
+ soup_message_queue_item_unref (item);
+ return stream;
+}
+
+/**
+ * soup_session_request:
+ * @session: a #SoupSession
+ * @uri_string: a URI, in string form
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a #SoupRequest for retrieving @uri_string.
+ *
+ * Return value: (transfer full): a new #SoupRequest, or
+ * %NULL on error.
+ *
+ * Since: 2.42
+ */
+SoupRequest *
+soup_session_request (SoupSession *session, const char *uri_string,
+ GError **error)
+{
+ SoupURI *uri;
+ SoupRequest *req;
+
+ uri = soup_uri_new (uri_string);
+ if (!uri) {
+ g_set_error (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_BAD_URI,
+ _("Could not parse URI '%s'"), uri_string);
+ return NULL;
+ }
+
+ req = soup_session_request_uri (session, uri, error);
+ soup_uri_free (uri);
+ return req;
+}
+
+/**
+ * soup_session_request_uri:
+ * @session: a #SoupSession
+ * @uri: a #SoupURI representing the URI to retrieve
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a #SoupRequest for retrieving @uri.
+ *
+ * Return value: (transfer full): a new #SoupRequest, or
+ * %NULL on error.
+ *
+ * Since: 2.42
+ */
+SoupRequest *
+soup_session_request_uri (SoupSession *session, SoupURI *uri,
+ GError **error)
+{
+ SoupSessionPrivate *priv;
+ GType request_type;
+
+ g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
+
+ priv = SOUP_SESSION_GET_PRIVATE (session);
+
+ request_type = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (priv->request_types, uri->scheme));
+ if (!request_type) {
+ g_set_error (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
+ _("Unsupported URI scheme '%s'"), uri->scheme);
+ return NULL;
+ }
+
+ return g_initable_new (request_type, NULL, error,
+ "uri", uri,
+ "session", session,
+ NULL);
+}
+
+static SoupRequestHTTP *
+initialize_http_request (SoupRequest *req,
+ const char *method,
+ GError **error)
+{
+ SoupRequestHTTP *http;
+ SoupMessage *msg;
+
+ if (!SOUP_IS_REQUEST_HTTP (req)) {
+ g_object_unref (req);
+ g_set_error (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_BAD_URI,
+ _("Not an HTTP URI"));
+ return NULL;
+ }
+
+ http = SOUP_REQUEST_HTTP (req);
+ msg = soup_request_http_get_message (http);
+ g_object_set (G_OBJECT (msg),
+ SOUP_MESSAGE_METHOD, method,
+ NULL);
+ g_object_unref (msg);
+
+ return http;
+}
+
+/**
+ * soup_session_request_http:
+ * @session: a #SoupSession
+ * @method: an HTTP method
+ * @uri_string: a URI, in string form
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a #SoupRequest for retrieving @uri_string, which must be an
+ * "http" or "https" URI (or another protocol listed in @session's
+ * #SoupSession:http-aliases or #SoupSession:https-aliases).
+ *
+ * Return value: (transfer full): a new #SoupRequestHTTP, or
+ * %NULL on error.
+ *
+ * Since: 2.42
+ */
+SoupRequestHTTP *
+soup_session_request_http (SoupSession *session,
+ const char *method,
+ const char *uri_string,
+ GError **error)
+{
+ SoupRequest *req;
+
+ req = soup_session_request (session, uri_string, error);
+ if (!req)
+ return NULL;
+
+ return initialize_http_request (req, method, error);
+}
+
+/**
+ * soup_session_request_http_uri:
+ * @session: a #SoupSession
+ * @method: an HTTP method
+ * @uri: a #SoupURI representing the URI to retrieve
+ * @error: return location for a #GError, or %NULL
+ *
+ * Creates a #SoupRequest for retrieving @uri, which must be an
+ * "http" or "https" URI (or another protocol listed in @session's
+ * #SoupSession:http-aliases or #SoupSession:https-aliases).
+ *
+ * Return value: (transfer full): a new #SoupRequestHTTP, or
+ * %NULL on error.
+ *
+ * Since: 2.42
+ */
+SoupRequestHTTP *
+soup_session_request_http_uri (SoupSession *session,
+ const char *method,
+ SoupURI *uri,
+ GError **error)
+{
+ SoupRequest *req;
+
+ req = soup_session_request_uri (session, uri, error);
+ if (!req)
+ return NULL;
+
+ return initialize_http_request (req, method, error);
+}
+
+/**
+ * SOUP_REQUEST_ERROR:
+ *
+ * A #GError domain for #SoupRequest<!-- -->-related errors. Used with
+ * #SoupRequestError.
+ *
+ * Since: 2.42
+ */
+/**
+ * SoupRequestError:
+ * @SOUP_REQUEST_ERROR_BAD_URI: the URI could not be parsed
+ * @SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME: the URI scheme is not
+ * supported by this #SoupSession
+ * @SOUP_REQUEST_ERROR_PARSING: the server's response could not
+ * be parsed
+ * @SOUP_REQUEST_ERROR_ENCODING: the server's response was in an
+ * unsupported format
+ *
+ * A #SoupRequest error.
+ *
+ * Since: 2.42
+ */
+
+GQuark
+soup_request_error_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("soup_request_error_quark");
+ return error;
}
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 8748a764..67a59eaa 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -60,7 +60,9 @@ typedef struct {
GType soup_session_get_type (void);
+#define SOUP_SESSION_LOCAL_ADDRESS "local-address"
#define SOUP_SESSION_PROXY_URI "proxy-uri"
+#define SOUP_SESSION_PROXY_RESOLVER "proxy-resolver"
#define SOUP_SESSION_MAX_CONNS "max-conns"
#define SOUP_SESSION_MAX_CONNS_PER_HOST "max-conns-per-host"
#define SOUP_SESSION_USE_NTLM "use-ntlm"
@@ -81,7 +83,12 @@ GType soup_session_get_type (void);
#define SOUP_SESSION_HTTP_ALIASES "http-aliases"
#define SOUP_SESSION_HTTPS_ALIASES "https-aliases"
-GMainContext *soup_session_get_async_context(SoupSession *session);
+SOUP_AVAILABLE_IN_2_42
+SoupSession *soup_session_new (void);
+
+SOUP_AVAILABLE_IN_2_42
+SoupSession *soup_session_new_with_options (const char *optname1,
+ ...) G_GNUC_NULL_TERMINATED;
void soup_session_queue_message (SoupSession *session,
SoupMessage *msg,
@@ -103,36 +110,101 @@ void soup_session_cancel_message (SoupSession *session,
guint status_code);
void soup_session_abort (SoupSession *session);
+GMainContext *soup_session_get_async_context(SoupSession *session);
+
+SOUP_AVAILABLE_IN_2_42
+void soup_session_send_async (SoupSession *session,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+SOUP_AVAILABLE_IN_2_42
+GInputStream *soup_session_send_finish (SoupSession *session,
+ GAsyncResult *result,
+ GError **error);
+SOUP_AVAILABLE_IN_2_42
+GInputStream *soup_session_send (SoupSession *session,
+ SoupMessage *msg,
+ GCancellable *cancellable,
+ GError **error);
+
+#ifndef SOUP_DISABLE_DEPRECATED
+/* SOUP_AVAILABLE_IN_2_30 -- this trips up gtkdoc-scan */
+SOUP_DEPRECATED_IN_2_38_FOR (soup_session_prefetch_dns)
void soup_session_prepare_for_uri (SoupSession *session,
SoupURI *uri);
+#endif
+SOUP_AVAILABLE_IN_2_38
void soup_session_prefetch_dns (SoupSession *session,
const char *hostname,
GCancellable *cancellable,
SoupAddressCallback callback,
gpointer user_data);
+SOUP_AVAILABLE_IN_2_38
gboolean soup_session_would_redirect (SoupSession *session,
SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_38
gboolean soup_session_redirect_message (SoupSession *session,
SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_24
void soup_session_add_feature (SoupSession *session,
SoupSessionFeature *feature);
+SOUP_AVAILABLE_IN_2_24
void soup_session_add_feature_by_type (SoupSession *session,
GType feature_type);
+SOUP_AVAILABLE_IN_2_24
void soup_session_remove_feature (SoupSession *session,
SoupSessionFeature *feature);
+SOUP_AVAILABLE_IN_2_24
void soup_session_remove_feature_by_type (SoupSession *session,
GType feature_type);
+SOUP_AVAILABLE_IN_2_42
+gboolean soup_session_has_feature (SoupSession *session,
+ GType feature_type);
+SOUP_AVAILABLE_IN_2_26
GSList *soup_session_get_features (SoupSession *session,
GType feature_type);
+SOUP_AVAILABLE_IN_2_26
SoupSessionFeature *soup_session_get_feature (SoupSession *session,
GType feature_type);
+SOUP_AVAILABLE_IN_2_28
SoupSessionFeature *soup_session_get_feature_for_message(SoupSession *session,
GType feature_type,
SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_42
+SoupRequest *soup_session_request (SoupSession *session,
+ const char *uri_string,
+ GError **error);
+SOUP_AVAILABLE_IN_2_42
+SoupRequest *soup_session_request_uri (SoupSession *session,
+ SoupURI *uri,
+ GError **error);
+SOUP_AVAILABLE_IN_2_42
+SoupRequestHTTP *soup_session_request_http (SoupSession *session,
+ const char *method,
+ const char *uri_string,
+ GError **error);
+SOUP_AVAILABLE_IN_2_42
+SoupRequestHTTP *soup_session_request_http_uri (SoupSession *session,
+ const char *method,
+ SoupURI *uri,
+ GError **error);
+
+SOUP_AVAILABLE_IN_2_42
+GQuark soup_request_error_quark (void);
+#define SOUP_REQUEST_ERROR soup_request_error_quark ()
+
+typedef enum {
+ SOUP_REQUEST_ERROR_BAD_URI,
+ SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
+ SOUP_REQUEST_ERROR_PARSING,
+ SOUP_REQUEST_ERROR_ENCODING
+} SoupRequestError;
+
G_END_DECLS
#endif /* SOUP_SESSION_H */
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index 2ac359de..b9f1dfca 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -11,11 +11,12 @@
#include <string.h>
+#include <gio/gnetworking.h>
+
#include "soup-socket.h"
#include "soup.h"
#include "soup-filter-input-stream.h"
#include "soup-io-stream.h"
-#include "soup-marshal.h"
#include "soup-misc-private.h"
/**
@@ -58,7 +59,7 @@ enum {
PROP_CLEAN_DISPOSE,
PROP_TLS_CERTIFICATE,
PROP_TLS_ERRORS,
- PROP_USE_PROXY,
+ PROP_PROXY_RESOLVER,
LAST_PROP
};
@@ -70,6 +71,7 @@ typedef struct {
GInputStream *istream;
GOutputStream *ostream;
GTlsCertificateFlags tls_errors;
+ GProxyResolver *proxy_resolver;
guint non_blocking:1;
guint is_server:1;
@@ -78,7 +80,6 @@ typedef struct {
guint ssl_fallback:1;
guint clean_dispose:1;
guint use_thread_context:1;
- guint use_proxy:1;
gpointer ssl_creds;
GMainContext *async_context;
@@ -149,6 +150,8 @@ soup_socket_finalize (GObject *object)
g_clear_object (&priv->local_addr);
g_clear_object (&priv->remote_addr);
+ g_clear_object (&priv->proxy_resolver);
+
if (priv->watch_src) {
if (priv->clean_dispose && !priv->is_server)
g_warning ("Disposing socket %p during async op", object);
@@ -179,6 +182,7 @@ finish_socket_setup (SoupSocketPrivate *priv)
priv->ostream = g_object_ref (g_io_stream_get_output_stream (priv->iostream));
g_socket_set_timeout (priv->gsock, priv->timeout);
+ g_socket_set_option (priv->gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL);
}
static void
@@ -219,8 +223,8 @@ soup_socket_set_property (GObject *object, guint prop_id,
if (priv->conn)
g_socket_set_timeout (priv->gsock, priv->timeout);
break;
- case PROP_USE_PROXY:
- priv->use_proxy = g_value_get_boolean (value);
+ case PROP_PROXY_RESOLVER:
+ priv->proxy_resolver = g_value_dup_object (value);
break;
case PROP_CLEAN_DISPOSE:
priv->clean_dispose = g_value_get_boolean (value);
@@ -280,8 +284,8 @@ soup_socket_get_property (GObject *object, guint prop_id,
case PROP_TLS_ERRORS:
g_value_set_flags (value, priv->tls_errors);
break;
- case PROP_USE_PROXY:
- g_value_set_boolean (value, priv->use_proxy);
+ case PROP_PROXY_RESOLVER:
+ g_value_set_object (value, priv->proxy_resolver);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -317,7 +321,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SoupSocketClass, readable),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -333,7 +337,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SoupSocketClass, writable),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -349,7 +353,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SoupSocketClass, disconnected),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -369,7 +373,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupSocketClass, new_connection),
NULL, NULL,
- _soup_marshal_NONE__OBJECT,
+ NULL,
G_TYPE_NONE, 1,
SOUP_TYPE_SOCKET);
/**
@@ -544,14 +548,14 @@ soup_socket_class_init (SoupSocketClass *socket_class)
* Alias for the #SoupSocket:use-thread-context property. (Use
* g_main_context_get_thread_default())
*
- * Since: 2.36.1
+ * Since: 2.38
*/
/**
* SoupSocket:use-thread-context:
*
* Use g_main_context_get_thread_default().
*
- * Since: 2.36.1
+ * Since: 2.38
*/
g_object_class_install_property (
object_class, PROP_USE_THREAD_CONTEXT,
@@ -618,12 +622,12 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_PARAM_READABLE));
g_object_class_install_property (
- object_class, PROP_USE_PROXY,
- g_param_spec_boolean (SOUP_SOCKET_USE_PROXY,
- "Use proxy",
- "Use #GProxyResolver",
- FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ object_class, PROP_PROXY_RESOLVER,
+ g_param_spec_object (SOUP_SOCKET_PROXY_RESOLVER,
+ "Proxy resolver",
+ "GProxyResolver to use",
+ G_TYPE_PROXY_RESOLVER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
@@ -651,11 +655,11 @@ soup_socket_new (const char *optname1, ...)
}
static void
-proxy_socket_client_event (GSocketClient *client,
- GSocketClientEvent event,
- GSocketConnectable *connectable,
- GIOStream *connection,
- gpointer user_data)
+re_emit_socket_client_event (GSocketClient *client,
+ GSocketClientEvent event,
+ GSocketConnectable *connectable,
+ GIOStream *connection,
+ gpointer user_data)
{
SoupSocket *sock = user_data;
@@ -663,35 +667,115 @@ proxy_socket_client_event (GSocketClient *client,
event, connection);
}
+static gboolean
+socket_connect_finish (SoupSocket *sock, GSocketConnection *conn)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_clear_object (&priv->connect_cancel);
+
+ if (conn) {
+ priv->conn = (GIOStream *)conn;
+ priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
+ finish_socket_setup (priv);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
static guint
-socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error)
+socket_legacy_error (SoupSocket *sock, GError *error)
+{
+ guint status;
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ status = SOUP_STATUS_CANCELLED;
+ else if (error->domain == G_RESOLVER_ERROR)
+ status = SOUP_STATUS_CANT_RESOLVE;
+ else
+ status = SOUP_STATUS_CANT_CONNECT;
+
+ g_error_free (error);
+ return status;
+}
+
+static GSocketClient *
+new_socket_client (SoupSocket *sock)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GSocketClient *client = g_socket_client_new ();
- if (priv->connect_cancel) {
- GCancellable *cancellable = priv->connect_cancel;
+ g_signal_connect (client, "event",
+ G_CALLBACK (re_emit_socket_client_event), sock);
+ if (priv->proxy_resolver) {
+ g_socket_client_set_proxy_resolver (client, priv->proxy_resolver);
+ g_socket_client_add_application_proxy (client, "http");
+ } else
+ g_socket_client_set_enable_proxy (client, FALSE);
+ if (priv->timeout)
+ g_socket_client_set_timeout (client, priv->timeout);
- g_object_unref (priv->connect_cancel);
- priv->connect_cancel = NULL;
- if (g_cancellable_is_cancelled (cancellable))
- return SOUP_STATUS_CANCELLED;
- }
+ if (priv->local_addr) {
+ GSocketAddress *addr;
- if (error) {
- if (error->domain == G_RESOLVER_ERROR) {
- g_error_free (error);
- return SOUP_STATUS_CANT_RESOLVE;
- } else {
- g_error_free (error);
- return SOUP_STATUS_CANT_CONNECT;
- }
+ addr = soup_address_get_gsockaddr (priv->local_addr);
+ g_socket_client_set_local_address (client, addr);
+ g_object_unref (addr);
}
- priv->conn = (GIOStream *)conn;
- priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
- finish_socket_setup (priv);
+ return client;
+}
- return SOUP_STATUS_OK;
+static void
+async_connected (GObject *client, GAsyncResult *result, gpointer data)
+{
+ GTask *task = data;
+ SoupSocket *sock = g_task_get_source_object (task);
+ GSocketConnection *conn;
+ GError *error = NULL;
+
+ conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
+ result, &error);
+ if (socket_connect_finish (sock, conn))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+gboolean
+soup_socket_connect_finish_internal (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+void
+soup_socket_connect_async_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupSocketPrivate *priv;
+ GSocketClient *client;
+ GTask *task;
+
+ g_return_if_fail (SOUP_IS_SOCKET (sock));
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_if_fail (!priv->is_server);
+ g_return_if_fail (priv->gsock == NULL);
+ g_return_if_fail (priv->remote_addr != NULL);
+
+ priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+ task = g_task_new (sock, priv->connect_cancel, callback, user_data);
+
+ client = new_socket_client (sock);
+ g_socket_client_connect_async (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel,
+ async_connected, task);
+ g_object_unref (client);
}
/**
@@ -710,22 +794,25 @@ typedef struct {
} SoupSocketAsyncConnectData;
static void
-async_connected (GObject *client, GAsyncResult *result, gpointer data)
+legacy_connect_async_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- SoupSocketAsyncConnectData *sacd = data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+ SoupSocket *sock = SOUP_SOCKET (object);
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ SoupSocketAsyncConnectData *sacd = user_data;
GError *error = NULL;
- GSocketConnection *conn;
guint status;
if (priv->async_context && !priv->use_thread_context)
g_main_context_pop_thread_default (priv->async_context);
- conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
- result, &error);
- status = socket_connected (sacd->sock, conn, error);
+ if (soup_socket_connect_finish_internal (sock, result, &error))
+ status = SOUP_STATUS_OK;
+ else
+ status = socket_legacy_error (sock, error);
- sacd->callback (sacd->sock, status, sacd->user_data);
+ sacd->callback (sock, status, sacd->user_data);
g_object_unref (sacd->sock);
g_slice_free (SoupSocketAsyncConnectData, sacd);
}
@@ -751,10 +838,11 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
{
SoupSocketPrivate *priv;
SoupSocketAsyncConnectData *sacd;
- GSocketClient *client;
g_return_if_fail (SOUP_IS_SOCKET (sock));
priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_if_fail (!priv->is_server);
+ g_return_if_fail (priv->gsock == NULL);
g_return_if_fail (priv->remote_addr != NULL);
sacd = g_slice_new0 (SoupSocketAsyncConnectData);
@@ -762,25 +850,38 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
sacd->callback = callback;
sacd->user_data = user_data;
- priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
-
if (priv->async_context && !priv->use_thread_context)
g_main_context_push_thread_default (priv->async_context);
- client = g_socket_client_new ();
- g_signal_connect (client, "event",
- G_CALLBACK (proxy_socket_client_event), sock);
- if (priv->use_proxy)
- g_socket_client_add_application_proxy (client, "http");
- else
- g_socket_client_set_enable_proxy (client, FALSE);
- if (priv->timeout)
- g_socket_client_set_timeout (client, priv->timeout);
- g_socket_client_connect_async (client,
- G_SOCKET_CONNECTABLE (priv->remote_addr),
- priv->connect_cancel,
- async_connected, sacd);
+ soup_socket_connect_async_internal (sock, cancellable,
+ legacy_connect_async_cb,
+ sacd);
+}
+
+gboolean
+soup_socket_connect_sync_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupSocketPrivate *priv;
+ GSocketClient *client;
+ GSocketConnection *conn;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_val_if_fail (!priv->is_server, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (priv->gsock == NULL, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
+
+ priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+
+ client = new_socket_client (sock);
+ conn = g_socket_client_connect (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel, error);
g_object_unref (client);
+
+ return socket_connect_finish (sock, conn);
}
/**
@@ -800,8 +901,6 @@ guint
soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
{
SoupSocketPrivate *priv;
- GSocketClient *client;
- GSocketConnection *conn;
GError *error = NULL;
g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
@@ -810,29 +909,23 @@ soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
g_return_val_if_fail (priv->gsock == NULL, SOUP_STATUS_MALFORMED);
g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
- if (cancellable)
- g_object_ref (cancellable);
- else
- cancellable = g_cancellable_new ();
- priv->connect_cancel = cancellable;
-
- client = g_socket_client_new ();
- g_signal_connect (client, "event",
- G_CALLBACK (proxy_socket_client_event), sock);
- if (priv->use_proxy)
- g_socket_client_add_application_proxy (client, "http");
+ if (soup_socket_connect_sync_internal (sock, cancellable, &error))
+ return SOUP_STATUS_OK;
else
- g_socket_client_set_enable_proxy (client, FALSE);
- if (priv->timeout)
- g_socket_client_set_timeout (client, priv->timeout);
- conn = g_socket_client_connect (client,
- G_SOCKET_CONNECTABLE (priv->remote_addr),
- priv->connect_cancel, &error);
- g_object_unref (client);
-
- return socket_connected (sock, conn, error);
+ return socket_legacy_error (sock, error);
}
+/**
+ * soup_socket_get_fd:
+ * @sock: a #SoupSocket
+ *
+ * Gets @sock's underlying file descriptor.
+ *
+ * Note that fiddling with the file descriptor may break the
+ * #SoupSocket.
+ *
+ * Return value: @sock's file descriptor.
+ */
int
soup_socket_get_fd (SoupSocket *sock)
{
@@ -1012,37 +1105,11 @@ soup_socket_accept_certificate (GTlsConnection *conn, GTlsCertificate *cert,
return TRUE;
}
-/**
- * soup_socket_start_ssl:
- * @sock: the socket
- * @cancellable: a #GCancellable
- *
- * Starts using SSL on @socket.
- *
- * Return value: success or failure
- **/
-gboolean
-soup_socket_start_ssl (SoupSocket *sock, GCancellable *cancellable)
-{
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
-
- return soup_socket_start_proxy_ssl (sock, soup_address_get_name (priv->remote_addr), cancellable);
-}
-
-/**
- * soup_socket_start_proxy_ssl:
- * @sock: the socket
- * @ssl_host: hostname of the SSL server
- * @cancellable: a #GCancellable
- *
- * Starts using SSL on @socket, expecting to find a host named
- * @ssl_host.
- *
- * Return value: success or failure
- **/
-gboolean
-soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
- GCancellable *cancellable)
+static gboolean
+soup_socket_setup_ssl (SoupSocket *sock,
+ const char *ssl_host,
+ GCancellable *cancellable,
+ GError **error)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
GTlsBackend *backend = g_tls_backend_get_default ();
@@ -1050,7 +1117,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
if (G_IS_TLS_CONNECTION (priv->conn))
return TRUE;
- if (g_cancellable_is_cancelled (cancellable))
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
priv->ssl = TRUE;
@@ -1061,7 +1128,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
identity = g_network_address_new (ssl_host, 0);
conn = g_initable_new (g_tls_backend_get_client_connection_type (backend),
- NULL, NULL,
+ cancellable, error,
"base-io-stream", priv->conn,
"server-identity", identity,
"database", priv->ssl_creds,
@@ -1085,7 +1152,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
GTlsServerConnection *conn;
conn = g_initable_new (g_tls_backend_get_server_connection_type (backend),
- NULL, NULL,
+ cancellable, error,
"base-io-stream", priv->conn,
"certificate", priv->ssl_creds,
"use-system-certdb", FALSE,
@@ -1110,76 +1177,103 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
return TRUE;
}
+
+/**
+ * soup_socket_start_ssl:
+ * @sock: the socket
+ * @cancellable: a #GCancellable
+ *
+ * Starts using SSL on @socket.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_socket_start_ssl (SoupSocket *sock, GCancellable *cancellable)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ return soup_socket_setup_ssl (sock, soup_address_get_name (priv->remote_addr),
+ cancellable, NULL);
+}
-guint
+/**
+ * soup_socket_start_proxy_ssl:
+ * @sock: the socket
+ * @ssl_host: hostname of the SSL server
+ * @cancellable: a #GCancellable
+ *
+ * Starts using SSL on @socket, expecting to find a host named
+ * @ssl_host.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
+ GCancellable *cancellable)
+{
+ return soup_socket_setup_ssl (sock, ssl_host, cancellable, NULL);
+}
+
+gboolean
soup_socket_handshake_sync (SoupSocket *sock,
- GCancellable *cancellable)
+ const char *ssl_host,
+ GCancellable *cancellable,
+ GError **error)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
- GError *error = NULL;
- priv->ssl = TRUE;
- if (g_tls_connection_handshake (G_TLS_CONNECTION (priv->conn),
- cancellable, &error))
- return SOUP_STATUS_OK;
- else if (!priv->ssl_fallback &&
- g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
- g_error_free (error);
- return SOUP_STATUS_TLS_FAILED;
- } else {
- g_error_free (error);
- return SOUP_STATUS_SSL_FAILED;
- }
+ if (!soup_socket_setup_ssl (sock, ssl_host, cancellable, error))
+ return FALSE;
+
+ return g_tls_connection_handshake (G_TLS_CONNECTION (priv->conn),
+ cancellable, error);
}
static void
handshake_async_ready (GObject *source, GAsyncResult *result, gpointer user_data)
{
- SoupSocketAsyncConnectData *data = user_data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (data->sock);
+ GTask *task = user_data;
GError *error = NULL;
- guint status;
-
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_pop_thread_default (priv->async_context);
if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (source),
result, &error))
- status = SOUP_STATUS_OK;
- else if (!priv->ssl_fallback &&
- g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS))
- status = SOUP_STATUS_TLS_FAILED;
+ g_task_return_boolean (task, TRUE);
else
- status = SOUP_STATUS_SSL_FAILED;
- g_clear_error (&error);
-
- data->callback (data->sock, status, data->user_data);
- g_object_unref (data->sock);
- g_slice_free (SoupSocketAsyncConnectData, data);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
void
-soup_socket_handshake_async (SoupSocket *sock,
- GCancellable *cancellable,
- SoupSocketCallback callback,
- gpointer user_data)
+soup_socket_handshake_async (SoupSocket *sock,
+ const char *ssl_host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
- SoupSocketAsyncConnectData *data;
+ GTask *task;
+ GError *error = NULL;
- priv->ssl = TRUE;
+ task = g_task_new (sock, cancellable, callback, user_data);
- data = g_slice_new (SoupSocketAsyncConnectData);
- data->sock = g_object_ref (sock);
- data->callback = callback;
- data->user_data = user_data;
+ if (!soup_socket_setup_ssl (sock, ssl_host, cancellable, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_push_thread_default (priv->async_context);
g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->conn),
G_PRIORITY_DEFAULT,
cancellable, handshake_async_ready,
- data);
+ task);
+}
+
+gboolean
+soup_socket_handshake_finish (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
}
/**
@@ -1242,8 +1336,10 @@ soup_socket_disconnect (SoupSocket *sock)
*/
g_object_ref (sock);
- /* Give all readers a chance to notice the connection close */
- g_signal_emit (sock, signals[READABLE], 0);
+ if (priv->non_blocking) {
+ /* Give all readers a chance to notice the connection close */
+ g_signal_emit (sock, signals[READABLE], 0);
+ }
/* FIXME: can't disconnect until all data is read */
@@ -1278,6 +1374,9 @@ soup_socket_is_connected (SoupSocket *sock)
*
* Returns the #SoupAddress corresponding to the local end of @sock.
*
+ * Calling this method on an unconnected socket is considered to be
+ * an error, and produces undefined results.
+ *
* Return value: (transfer none): the #SoupAddress
**/
SoupAddress *
@@ -1293,13 +1392,25 @@ soup_socket_get_local_address (SoupSocket *sock)
GSocketAddress *addr;
struct sockaddr_storage sa;
gssize sa_len;
+ GError *error = NULL;
- addr = g_socket_get_local_address (priv->gsock, NULL);
+ if (priv->gsock == NULL) {
+ g_warning ("%s: socket not connected", G_STRLOC);
+ goto unlock;
+ }
+
+ addr = g_socket_get_local_address (priv->gsock, &error);
+ if (addr == NULL) {
+ g_warning ("%s: %s", G_STRLOC, error->message);
+ g_error_free (error);
+ goto unlock;
+ }
sa_len = g_socket_address_get_native_size (addr);
g_socket_address_to_native (addr, &sa, sa_len, NULL);
priv->local_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
g_object_unref (addr);
}
+unlock:
g_mutex_unlock (&priv->addrlock);
return priv->local_addr;
@@ -1311,6 +1422,9 @@ soup_socket_get_local_address (SoupSocket *sock)
*
* Returns the #SoupAddress corresponding to the remote end of @sock.
*
+ * Calling this method on an unconnected socket is considered to be
+ * an error, and produces undefined results.
+ *
* Return value: (transfer none): the #SoupAddress
**/
SoupAddress *
@@ -1326,13 +1440,25 @@ soup_socket_get_remote_address (SoupSocket *sock)
GSocketAddress *addr;
struct sockaddr_storage sa;
gssize sa_len;
+ GError *error = NULL;
+
+ if (priv->gsock == NULL) {
+ g_warning ("%s: socket not connected", G_STRLOC);
+ goto unlock;
+ }
- addr = g_socket_get_remote_address (priv->gsock, NULL);
+ addr = g_socket_get_remote_address (priv->gsock, &error);
+ if (addr == NULL) {
+ g_warning ("%s: %s", G_STRLOC, error->message);
+ g_error_free (error);
+ goto unlock;
+ }
sa_len = g_socket_address_get_native_size (addr);
g_socket_address_to_native (addr, &sa, sa_len, NULL);
priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
g_object_unref (addr);
}
+unlock:
g_mutex_unlock (&priv->addrlock);
return priv->remote_addr;
@@ -1344,18 +1470,24 @@ soup_socket_get_http_proxy_uri (SoupSocket *sock)
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
GSocketAddress *addr;
GProxyAddress *paddr;
+ SoupURI *uri;
if (!priv->gsock)
return NULL;
addr = g_socket_get_remote_address (priv->gsock, NULL);
- if (!addr || !G_IS_PROXY_ADDRESS (addr))
+ if (!addr || !G_IS_PROXY_ADDRESS (addr)) {
+ if (addr)
+ g_object_unref (addr);
return NULL;
+ }
paddr = G_PROXY_ADDRESS (addr);
if (strcmp (g_proxy_address_get_protocol (paddr), "http") != 0)
return NULL;
- return soup_uri_new (g_proxy_address_get_uri (paddr));
+ uri = soup_uri_new (g_proxy_address_get_uri (paddr));
+ g_object_unref (addr);
+ return uri;
}
static gboolean
@@ -1530,7 +1662,7 @@ soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
SOUP_FILTER_INPUT_STREAM (priv->istream),
buffer, len, boundary, boundary_len,
!priv->non_blocking,
- got_boundary, cancellable, &my_err);
+ TRUE, got_boundary, cancellable, &my_err);
status = translate_read_status (sock, cancellable,
my_nread, nread, my_err, error);
}
diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h
index ed405bdc..5c1264f9 100644
--- a/libsoup/soup-socket.h
+++ b/libsoup/soup-socket.h
@@ -26,11 +26,11 @@ typedef struct {
GObjectClass parent_class;
/* signals */
- void (*readable) (SoupSocket *);
- void (*writable) (SoupSocket *);
- void (*disconnected) (SoupSocket *);
+ void (*readable) (SoupSocket *sock);
+ void (*writable) (SoupSocket *sock);
+ void (*disconnected) (SoupSocket *sock);
- void (*new_connection) (SoupSocket *, SoupSocket *);
+ void (*new_connection) (SoupSocket *listener, SoupSocket *new_sock);
/* Padding for future expansion */
void (*_libsoup_reserved1) (void);
diff --git a/libsoup/soup-status.c b/libsoup/soup-status.c
index 7b048a80..6dbb9edb 100644
--- a/libsoup/soup-status.c
+++ b/libsoup/soup-status.c
@@ -68,7 +68,7 @@
**/
/**
- * SoupKnownStatusCode:
+ * SoupStatus:
* @SOUP_STATUS_NONE: No status available. (Eg, the message has not
* been sent yet)
* @SOUP_STATUS_CANCELLED: Message was cancelled locally
@@ -150,14 +150,21 @@
*
* These represent the known HTTP status code values, plus various
* network and internal errors.
+ *
+ * Note that no libsoup functions take or return this type directly;
+ * any function that works with status codes will accept unrecognized
+ * status codes as well.
+ *
+ * Prior to 2.44 this type was called
+ * <literal>SoupKnownStatusCode</literal>, but the individual values
+ * have always had the names they have now.
**/
/**
* SOUP_HTTP_ERROR:
*
- * A #GError domain representing an HTTP status. Use a
- * #SoupKnownStatusCode for the <structfield>code</structfield>
- * value.
+ * A #GError domain representing an HTTP status. Use a #SoupStatus for
+ * the <structfield>code</structfield> value.
**/
diff --git a/libsoup/soup-status.h b/libsoup/soup-status.h
index fc8d5a36..fb4147c6 100644
--- a/libsoup/soup-status.h
+++ b/libsoup/soup-status.h
@@ -8,7 +8,7 @@
#ifndef SOUP_STATUS_H
#define SOUP_STATUS_H 1
-#include <glib.h>
+#include <libsoup/soup-types.h>
G_BEGIN_DECLS
@@ -91,14 +91,93 @@ typedef enum {
SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
SOUP_STATUS_INSUFFICIENT_STORAGE = 507, /* WebDAV search */
SOUP_STATUS_NOT_EXTENDED = 510 /* RFC 2774 */
-} SoupKnownStatusCode;
+} SoupStatus;
const char *soup_status_get_phrase (guint status_code);
+SOUP_AVAILABLE_IN_2_26
guint soup_status_proxify (guint status_code);
#define SOUP_HTTP_ERROR soup_http_error_quark()
GQuark soup_http_error_quark (void);
+#ifndef SOUP_DISABLE_DEPRECATED
+/* For introspection purposes, we create a duplicate enum type under
+ * the deprecated type name, since some (all?) bindings don't handle
+ * enum type typedefs the way we want.
+ */
+typedef enum {
+ SOUP_KNOWN_STATUS_CODE_NONE,
+
+ SOUP_KNOWN_STATUS_CODE_CANCELLED = 1,
+ SOUP_KNOWN_STATUS_CODE_CANT_RESOLVE,
+ SOUP_KNOWN_STATUS_CODE_CANT_RESOLVE_PROXY,
+ SOUP_KNOWN_STATUS_CODE_CANT_CONNECT,
+ SOUP_KNOWN_STATUS_CODE_CANT_CONNECT_PROXY,
+ SOUP_KNOWN_STATUS_CODE_SSL_FAILED,
+ SOUP_KNOWN_STATUS_CODE_IO_ERROR,
+ SOUP_KNOWN_STATUS_CODE_MALFORMED,
+ SOUP_KNOWN_STATUS_CODE_TRY_AGAIN,
+ SOUP_KNOWN_STATUS_CODE_TOO_MANY_REDIRECTS,
+ SOUP_KNOWN_STATUS_CODE_TLS_FAILED,
+
+ SOUP_KNOWN_STATUS_CODE_CONTINUE = 100,
+ SOUP_KNOWN_STATUS_CODE_SWITCHING_PROTOCOLS = 101,
+ SOUP_KNOWN_STATUS_CODE_PROCESSING = 102,
+
+ SOUP_KNOWN_STATUS_CODE_OK = 200,
+ SOUP_KNOWN_STATUS_CODE_CREATED = 201,
+ SOUP_KNOWN_STATUS_CODE_ACCEPTED = 202,
+ SOUP_KNOWN_STATUS_CODE_NON_AUTHORITATIVE = 203,
+ SOUP_KNOWN_STATUS_CODE_NO_CONTENT = 204,
+ SOUP_KNOWN_STATUS_CODE_RESET_CONTENT = 205,
+ SOUP_KNOWN_STATUS_CODE_PARTIAL_CONTENT = 206,
+ SOUP_KNOWN_STATUS_CODE_MULTI_STATUS = 207,
+
+ SOUP_KNOWN_STATUS_CODE_MULTIPLE_CHOICES = 300,
+ SOUP_KNOWN_STATUS_CODE_MOVED_PERMANENTLY = 301,
+ SOUP_KNOWN_STATUS_CODE_FOUND = 302,
+ SOUP_KNOWN_STATUS_CODE_MOVED_TEMPORARILY = 302,
+ SOUP_KNOWN_STATUS_CODE_SEE_OTHER = 303,
+ SOUP_KNOWN_STATUS_CODE_NOT_MODIFIED = 304,
+ SOUP_KNOWN_STATUS_CODE_USE_PROXY = 305,
+ SOUP_KNOWN_STATUS_CODE_NOT_APPEARING_IN_THIS_PROTOCOL = 306,
+ SOUP_KNOWN_STATUS_CODE_TEMPORARY_REDIRECT = 307,
+
+ SOUP_KNOWN_STATUS_CODE_BAD_REQUEST = 400,
+ SOUP_KNOWN_STATUS_CODE_UNAUTHORIZED = 401,
+ SOUP_KNOWN_STATUS_CODE_PAYMENT_REQUIRED = 402,
+ SOUP_KNOWN_STATUS_CODE_FORBIDDEN = 403,
+ SOUP_KNOWN_STATUS_CODE_NOT_FOUND = 404,
+ SOUP_KNOWN_STATUS_CODE_METHOD_NOT_ALLOWED = 405,
+ SOUP_KNOWN_STATUS_CODE_NOT_ACCEPTABLE = 406,
+ SOUP_KNOWN_STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
+ SOUP_KNOWN_STATUS_CODE_PROXY_UNAUTHORIZED = SOUP_KNOWN_STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED,
+ SOUP_KNOWN_STATUS_CODE_REQUEST_TIMEOUT = 408,
+ SOUP_KNOWN_STATUS_CODE_CONFLICT = 409,
+ SOUP_KNOWN_STATUS_CODE_GONE = 410,
+ SOUP_KNOWN_STATUS_CODE_LENGTH_REQUIRED = 411,
+ SOUP_KNOWN_STATUS_CODE_PRECONDITION_FAILED = 412,
+ SOUP_KNOWN_STATUS_CODE_REQUEST_ENTITY_TOO_LARGE = 413,
+ SOUP_KNOWN_STATUS_CODE_REQUEST_URI_TOO_LONG = 414,
+ SOUP_KNOWN_STATUS_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
+ SOUP_KNOWN_STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
+ SOUP_KNOWN_STATUS_CODE_INVALID_RANGE = SOUP_KNOWN_STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE,
+ SOUP_KNOWN_STATUS_CODE_EXPECTATION_FAILED = 417,
+ SOUP_KNOWN_STATUS_CODE_UNPROCESSABLE_ENTITY = 422,
+ SOUP_KNOWN_STATUS_CODE_LOCKED = 423,
+ SOUP_KNOWN_STATUS_CODE_FAILED_DEPENDENCY = 424,
+
+ SOUP_KNOWN_STATUS_CODE_INTERNAL_SERVER_ERROR = 500,
+ SOUP_KNOWN_STATUS_CODE_NOT_IMPLEMENTED = 501,
+ SOUP_KNOWN_STATUS_CODE_BAD_GATEWAY = 502,
+ SOUP_KNOWN_STATUS_CODE_SERVICE_UNAVAILABLE = 503,
+ SOUP_KNOWN_STATUS_CODE_GATEWAY_TIMEOUT = 504,
+ SOUP_KNOWN_STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
+ SOUP_KNOWN_STATUS_CODE_INSUFFICIENT_STORAGE = 507,
+ SOUP_KNOWN_STATUS_CODE_NOT_EXTENDED = 510
+} SoupKnownStatusCode;
+#endif
+
G_END_DECLS
#endif /* SOUP_STATUS_H */
diff --git a/libsoup/soup-tld.c b/libsoup/soup-tld.c
index 0c40b675..2e3da62d 100644
--- a/libsoup/soup-tld.c
+++ b/libsoup/soup-tld.c
@@ -17,6 +17,15 @@
#include "soup.h"
#include "soup-tld-private.h"
+/**
+ * SECTION:soup-tld
+ * @short_description: Top-Level Domain Utilities
+ *
+ * These functions can be used to parse hostnames to attempt to determine
+ * what part of the name belongs to the domain owner, and what part is
+ * simply a "public suffix" such as ".com".
+ */
+
static void soup_tld_ensure_rules_hash_table (void);
static const char *soup_tld_get_base_domain_internal (const char *hostname,
guint additional_domains,
@@ -27,8 +36,7 @@ static SoupTLDEntry tld_entries[] = {
#include "tld_data.inc"
};
-/**
- * Stores the entries data in a hash table to ease and speed up
+/* Stores the entries data in a hash table to ease and speed up
* searches.
*/
static void
@@ -49,8 +57,7 @@ soup_tld_ensure_rules_hash_table (void)
/**
* soup_tld_get_base_domain:
- * @tld: a #SoupTLD
- * @hostname: a UTF-8 hostname in its canonical representation form
+ * @hostname: a hostname
* @error: return location for a #GError, or %NULL to ignore
* errors. See #SoupTLDError for the available error codes
*
@@ -59,9 +66,14 @@ soup_tld_ensure_rules_hash_table (void)
* plus the second level domain, for example for myhost.mydomain.com
* it will return mydomain.com.
*
- * This method only works for valid UTF-8 hostnames in their canonical
- * representation form, so you should use g_hostname_to_unicode() to
- * get the canonical representation if that is not the case.
+ * Note that %NULL will be returned for private URLs (those not ending
+ * with any well known TLD) because choosing a base domain for them
+ * would be totally arbitrary.
+ *
+ * Prior to libsoup 2.46, this function required that @hostname be in
+ * UTF-8 if it was an IDN. From 2.46 on, the name can be in either
+ * UTF-8 or ASCII format (and the return value will be in the same
+ * format).
*
* Returns: a pointer to the start of the base domain in @hostname. If
* an error occurs, %NULL will be returned and @error set.
@@ -72,22 +84,21 @@ const char *
soup_tld_get_base_domain (const char *hostname, GError **error)
{
g_return_val_if_fail (hostname, NULL);
- g_return_val_if_fail (!g_hostname_is_ascii_encoded (hostname), FALSE);
return soup_tld_get_base_domain_internal (hostname, 1, error);
}
/**
* soup_tld_domain_is_public_suffix:
- * @tld: a #SoupTLD
- * @domain: a UTF-8 domain in its canonical representation form
+ * @domain: a domain name
*
* Looks whether the @domain passed as argument is a public domain
* suffix (.org, .com, .co.uk, etc) or not.
*
- * This method only works for valid UTF-8 domains in their canonical
- * representation form, so you should use g_hostname_to_unicode() to
- * get the canonical representation if that is not the case.
+ * Prior to libsoup 2.46, this function required that @domain be in
+ * UTF-8 if it was an IDN. From 2.46 on, the name can be in either
+ * UTF-8 or ASCII format (and the return value will be in the same
+ * format).
*
* Returns: %TRUE if it is a public domain, %FALSE otherwise.
*
@@ -102,12 +113,14 @@ soup_tld_domain_is_public_suffix (const char *domain)
g_return_val_if_fail (domain, FALSE);
/* Skip the leading '.' if present */
- if (*domain == '.' && !(++domain))
+ if (*domain == '.' && !*(++domain))
g_return_val_if_reached (FALSE);
base_domain = soup_tld_get_base_domain_internal (domain, 0, &error);
- if (base_domain)
+ if (g_strcmp0 (domain, base_domain)) {
+ g_clear_error (&error);
return FALSE;
+ }
if (g_error_matches (error, SOUP_TLD_ERROR, SOUP_TLD_ERROR_NO_BASE_DOMAIN)) {
g_error_free (error);
@@ -125,6 +138,31 @@ soup_tld_domain_is_public_suffix (const char *domain)
return TRUE;
}
+/**
+ * SOUP_TLD_ERROR:
+ *
+ * The #GError domain for soup-tld-related errors.
+ *
+ * Since: 2.40
+ */
+/**
+ * SoupTLDError:
+ * @SOUP_TLD_ERROR_INVALID_HOSTNAME: A hostname was syntactically
+ * invalid.
+ * @SOUP_TLD_ERROR_IS_IP_ADDRESS: The passed-in "hostname" was
+ * actually an IP address (and thus has no base domain or
+ * public suffix).
+ * @SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS: The passed-in hostname
+ * did not have enough components. Eg, calling
+ * soup_tld_get_base_domain() on <literal>"co.uk"</literal>.
+ * @SOUP_TLD_ERROR_NO_BASE_DOMAIN: The passed-in hostname has
+ * no recognized public suffix.
+ *
+ * Error codes for %SOUP_TLD_ERROR.
+ *
+ * Since: 2.40
+ */
+
GQuark
soup_tld_error_quark (void)
{
@@ -137,8 +175,10 @@ soup_tld_error_quark (void)
static const char *
soup_tld_get_base_domain_internal (const char *hostname, guint additional_domains, GError **error)
{
- char *prev_domain, *cur_domain, *tld, *next_dot;
+ char *prev_domain, *cur_domain, *next_dot;
gint add_domains;
+ const char *orig_hostname = NULL, *tld;
+ char *utf8_hostname = NULL;
soup_tld_ensure_rules_hash_table ();
@@ -149,6 +189,17 @@ soup_tld_get_base_domain_internal (const char *hostname, guint additional_domain
return NULL;
}
+ if (g_hostname_is_ascii_encoded (hostname)) {
+ orig_hostname = hostname;
+ hostname = utf8_hostname = g_hostname_to_unicode (hostname);
+ if (!hostname) {
+ g_set_error_literal (error, SOUP_TLD_ERROR,
+ SOUP_TLD_ERROR_INVALID_HOSTNAME,
+ _("Invalid hostname"));
+ return NULL;
+ }
+ }
+
cur_domain = (char *) hostname;
tld = cur_domain;
prev_domain = NULL;
@@ -167,6 +218,7 @@ soup_tld_get_base_domain_internal (const char *hostname, guint additional_domain
g_set_error_literal (error, SOUP_TLD_ERROR,
SOUP_TLD_ERROR_INVALID_HOSTNAME,
_("Invalid hostname"));
+ g_free (utf8_hostname);
return NULL;
}
@@ -178,15 +230,9 @@ soup_tld_get_base_domain_internal (const char *hostname, guint additional_domain
/* If we match a *. rule and there were no previous exceptions
* nor previous domains then treat it as an exact match.
*/
- if (!prev_domain) {
- g_set_error_literal (error, SOUP_TLD_ERROR,
- SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS,
- _("Not enough domains"));
- return NULL;
- }
- tld = prev_domain;
+ tld = prev_domain ? prev_domain : cur_domain;
break;
- } else if (*flags == SOUP_TLD_RULE_NORMAL || !next_dot) {
+ } else if (*flags == SOUP_TLD_RULE_NORMAL) {
tld = cur_domain;
break;
} else if (*flags & SOUP_TLD_RULE_EXCEPTION) {
@@ -202,6 +248,7 @@ soup_tld_get_base_domain_internal (const char *hostname, guint additional_domain
g_set_error_literal (error, SOUP_TLD_ERROR,
SOUP_TLD_ERROR_NO_BASE_DOMAIN,
_("Hostname has no base domain"));
+ g_free (utf8_hostname);
return NULL;
}
@@ -209,6 +256,37 @@ soup_tld_get_base_domain_internal (const char *hostname, guint additional_domain
cur_domain = next_dot + 1;
}
+ if (orig_hostname) {
+ int dots;
+ const char *p;
+
+ /* Count the number of dots that appear after tld in
+ * utf8_hostname, and then find the corresponding spot
+ * in orig_hostname;
+ */
+ for (p = tld, dots = 0; *p; p++) {
+ if (*p == '.')
+ dots++;
+ }
+
+ for (p = orig_hostname + strlen (orig_hostname); p > orig_hostname; p--) {
+ if (*(p - 1) == '.') {
+ if (dots)
+ dots--;
+ else
+ break;
+ }
+ }
+ /* It's not possible for utf8_hostname to have had
+ * more dots than orig_hostname.
+ */
+ g_assert (dots == 0);
+
+ tld = p;
+ g_free (utf8_hostname);
+ hostname = orig_hostname;
+ }
+
/* Include the additional number of domains requested. */
add_domains = additional_domains;
while (tld != hostname) {
diff --git a/libsoup/soup-tld.h b/libsoup/soup-tld.h
index 38de46a6..4b099a26 100644
--- a/libsoup/soup-tld.h
+++ b/libsoup/soup-tld.h
@@ -10,14 +10,17 @@
G_BEGIN_DECLS
+SOUP_AVAILABLE_IN_2_40
const char *soup_tld_get_base_domain (const char *hostname,
GError **error);
+SOUP_AVAILABLE_IN_2_40
gboolean soup_tld_domain_is_public_suffix (const char *domain);
/* Errors */
-#define SOUP_TLD_ERROR soup_tld_error_quark()
+SOUP_AVAILABLE_IN_2_40
GQuark soup_tld_error_quark (void);
+#define SOUP_TLD_ERROR soup_tld_error_quark()
typedef enum {
SOUP_TLD_ERROR_INVALID_HOSTNAME,
diff --git a/libsoup/soup-types.h b/libsoup/soup-types.h
index d0220399..0776bdbf 100644
--- a/libsoup/soup-types.h
+++ b/libsoup/soup-types.h
@@ -8,6 +8,7 @@
#include <gio/gio.h>
+#include <libsoup/soup-version.h>
#include <libsoup/soup-status.h>
G_BEGIN_DECLS
@@ -19,6 +20,8 @@ typedef struct _SoupCookie SoupCookie;
typedef struct _SoupCookieJar SoupCookieJar;
typedef struct _SoupDate SoupDate;
typedef struct _SoupMessage SoupMessage;
+typedef struct _SoupRequest SoupRequest;
+typedef struct _SoupRequestHTTP SoupRequestHTTP;
typedef struct _SoupServer SoupServer;
typedef struct _SoupSession SoupSession;
typedef struct _SoupSessionAsync SoupSessionAsync;
diff --git a/libsoup/soup-uri.c b/libsoup/soup-uri.c
index 4be679d5..42c2f680 100644
--- a/libsoup/soup-uri.c
+++ b/libsoup/soup-uri.c
@@ -105,12 +105,63 @@
* Since: 2.24
**/
+/**
+ * SOUP_URI_SCHEME_HTTP:
+ *
+ * "http" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ */
+/**
+ * SOUP_URI_SCHEME_HTTPS:
+ *
+ * "https" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ */
+/**
+ * SOUP_URI_SCHEME_FTP:
+ *
+ * "ftp" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.30
+ */
+/**
+ * SOUP_URI_SCHEME_FILE:
+ *
+ * "file" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.30
+ */
+/**
+ * SOUP_URI_SCHEME_DATA:
+ *
+ * "data" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.30
+ */
+/**
+ * SOUP_URI_SCHEME_RESOURCE:
+ *
+ * "data" as an interned string; you can compare this directly to a
+ * #SoupURI's <literal>scheme</literal> field using
+ * <literal>==</literal>.
+ *
+ * Since: 2.42
+ */
+
static void append_uri_encoded (GString *str, const char *in, const char *extra_enc_chars);
static char *uri_normalized_copy (const char *str, int length, const char *unescape_extra);
gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
gpointer _SOUP_URI_SCHEME_FTP;
-gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
+gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE;
static inline const char *
soup_uri_parse_scheme (const char *scheme, int len)
@@ -119,6 +170,8 @@ soup_uri_parse_scheme (const char *scheme, int len)
return SOUP_URI_SCHEME_HTTP;
} else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) {
return SOUP_URI_SCHEME_HTTPS;
+ } else if (len == 8 && !g_ascii_strncasecmp (scheme, "resource", len)) {
+ return SOUP_URI_SCHEME_RESOURCE;
} else {
char *lower_scheme;
@@ -210,10 +263,13 @@ soup_uri_new_with_base (SoupURI *base, const char *uri_string)
end = hash;
}
- /* Find scheme: initial [a-z+.-]* substring until ":" */
+ /* Find scheme */
p = uri_string;
while (p < end && (g_ascii_isalpha (*p) ||
- *p == '.' || *p == '+' || *p == '-'))
+ (p > uri_string && (g_ascii_isdigit (*p) ||
+ *p == '.' ||
+ *p == '+' ||
+ *p == '-'))))
p++;
if (p > uri_string && *p == ':') {
@@ -237,21 +293,23 @@ soup_uri_new_with_base (SoupURI *base, const char *uri_string)
if (at && at < path) {
colon = strchr (uri_string, ':');
if (colon && colon < at) {
- uri->password = uri_decoded_copy (colon + 1,
- at - colon - 1);
+ uri->password = soup_uri_decoded_copy (colon + 1,
+ at - colon - 1, NULL);
} else {
uri->password = NULL;
colon = at;
}
- uri->user = uri_decoded_copy (uri_string,
- colon - uri_string);
+ uri->user = soup_uri_decoded_copy (uri_string,
+ colon - uri_string, NULL);
uri_string = at + 1;
} else
uri->user = uri->password = NULL;
/* Find host and port. */
if (*uri_string == '[') {
+ const char *pct;
+
uri_string++;
hostend = strchr (uri_string, ']');
if (!hostend || hostend > path) {
@@ -262,13 +320,20 @@ soup_uri_new_with_base (SoupURI *base, const char *uri_string)
colon = hostend + 1;
else
colon = NULL;
+
+ pct = memchr (uri_string, '%', hostend - uri_string);
+ if (!pct || (pct[1] == '2' && pct[2] == '5')) {
+ uri->host = soup_uri_decoded_copy (uri_string,
+ hostend - uri_string, NULL);
+ } else
+ uri->host = g_strndup (uri_string, hostend - uri_string);
} else {
colon = memchr (uri_string, ':', path - uri_string);
hostend = colon ? colon : path;
+ uri->host = soup_uri_decoded_copy (uri_string,
+ hostend - uri_string, NULL);
}
- uri->host = uri_decoded_copy (uri_string, hostend - uri_string);
-
if (colon && colon != path - 1) {
char *portend;
uri->port = strtoul (colon + 1, &portend, 10);
@@ -436,21 +501,9 @@ soup_uri_new (const char *uri_string)
}
-/**
- * soup_uri_to_string:
- * @uri: a #SoupURI
- * @just_path_and_query: if %TRUE, output just the path and query portions
- *
- * Returns a string representing @uri.
- *
- * If @just_path_and_query is %TRUE, this concatenates the path and query
- * together. That is, it constructs the string that would be needed in
- * the Request-Line of an HTTP request for @uri.
- *
- * Return value: a string representing @uri, which the caller must free.
- **/
char *
-soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
+soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query,
+ gboolean force_port)
{
GString *str;
char *return_result;
@@ -458,7 +511,7 @@ soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
g_return_val_if_fail (uri != NULL, NULL);
g_warn_if_fail (SOUP_URI_IS_VALID (uri));
- str = g_string_sized_new (20);
+ str = g_string_sized_new (40);
if (uri->scheme && !just_path_and_query)
g_string_append_printf (str, "%s:", uri->scheme);
@@ -469,12 +522,20 @@ soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
g_string_append_c (str, '@');
}
if (strchr (uri->host, ':')) {
+ const char *pct;
+
g_string_append_c (str, '[');
- g_string_append (str, uri->host);
+ pct = strchr (uri->host, '%');
+ if (pct) {
+ g_string_append_printf (str, "%.*s%%25%s",
+ (int) (pct - uri->host),
+ uri->host, pct + 1);
+ } else
+ g_string_append (str, uri->host);
g_string_append_c (str, ']');
} else
append_uri_encoded (str, uri->host, ":/");
- if (uri->port && uri->port != soup_scheme_default_port (uri->scheme))
+ if (uri->port && (force_port || uri->port != soup_scheme_default_port (uri->scheme)))
g_string_append_printf (str, ":%u", uri->port);
if (!uri->path && (uri->query || uri->fragment))
g_string_append_c (str, '/');
@@ -505,6 +566,28 @@ soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
}
/**
+ * soup_uri_to_string:
+ * @uri: a #SoupURI
+ * @just_path_and_query: if %TRUE, output just the path and query portions
+ *
+ * Returns a string representing @uri.
+ *
+ * If @just_path_and_query is %TRUE, this concatenates the path and query
+ * together. That is, it constructs the string that would be needed in
+ * the Request-Line of an HTTP request for @uri.
+ *
+ * Note that the output will never contain a password, even if @uri
+ * does.
+ *
+ * Return value: a string representing @uri, which the caller must free.
+ **/
+char *
+soup_uri_to_string (SoupURI *uri, gboolean just_path_and_query)
+{
+ return soup_uri_to_string_internal (uri, just_path_and_query, FALSE);
+}
+
+/**
* soup_uri_copy:
* @uri: a #SoupURI
*
@@ -641,13 +724,14 @@ soup_uri_encode (const char *part, const char *escape_extra)
#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
char *
-uri_decoded_copy (const char *part, int length)
+soup_uri_decoded_copy (const char *part, int length, int *decoded_length)
{
unsigned char *s, *d;
- char *decoded = g_strndup (part, length);
+ char *decoded;
g_return_val_if_fail (part != NULL, NULL);
+ decoded = g_strndup (part, length);
s = d = (unsigned char *)decoded;
do {
if (*s == '%') {
@@ -662,6 +746,9 @@ uri_decoded_copy (const char *part, int length)
*d++ = *s;
} while (*s++);
+ if (decoded_length)
+ *decoded_length = d - (unsigned char *)decoded - 1;
+
return decoded;
}
@@ -682,7 +769,7 @@ soup_uri_decode (const char *part)
{
g_return_val_if_fail (part != NULL, NULL);
- return uri_decoded_copy (part, strlen (part));
+ return soup_uri_decoded_copy (part, strlen (part), NULL);
}
static char *
@@ -707,7 +794,7 @@ uri_normalized_copy (const char *part, int length,
c = HEXCHAR (s);
if (soup_char_is_uri_unreserved (c) ||
- strchr (unescape_extra, c)) {
+ (c && strchr (unescape_extra, c))) {
*d++ = c;
s += 3;
} else {
@@ -801,20 +888,6 @@ soup_uri_uses_default_port (SoupURI *uri)
}
/**
- * SOUP_URI_SCHEME_HTTP:
- *
- * "http" as an interned string. This can be compared directly against
- * the value of a #SoupURI's <structfield>scheme</structfield>
- **/
-
-/**
- * SOUP_URI_SCHEME_HTTPS:
- *
- * "https" as an interned string. This can be compared directly
- * against the value of a #SoupURI's <structfield>scheme</structfield>
- **/
-
-/**
* soup_uri_get_scheme:
* @uri: a #SoupURI
*
@@ -1152,7 +1225,7 @@ soup_uri_set_fragment (SoupURI *uri, const char *fragment)
*
* Return value: the new #SoupURI
*
- * Since: 2.26.3
+ * Since: 2.28
**/
SoupURI *
soup_uri_copy_host (SoupURI *uri)
@@ -1179,7 +1252,7 @@ soup_uri_copy_host (SoupURI *uri)
*
* Return value: a hash
*
- * Since: 2.26.3
+ * Since: 2.28
**/
guint
soup_uri_host_hash (gconstpointer key)
@@ -1203,7 +1276,7 @@ soup_uri_host_hash (gconstpointer key)
* Return value: whether or not the URIs are equal in scheme, host,
* and port.
*
- * Since: 2.26.3
+ * Since: 2.28
**/
gboolean
soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
@@ -1224,4 +1297,47 @@ soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
return g_ascii_strcasecmp (one->host, two->host) == 0;
}
+gboolean
+soup_uri_is_http (SoupURI *uri, char **aliases)
+{
+ int i;
+
+ if (uri->scheme == SOUP_URI_SCHEME_HTTP)
+ return TRUE;
+ else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
+ return FALSE;
+ else if (!aliases)
+ return FALSE;
+
+ for (i = 0; aliases[i]; i++) {
+ if (uri->scheme == aliases[i])
+ return TRUE;
+ }
+
+ if (!aliases[1] && !strcmp (aliases[0], "*"))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean
+soup_uri_is_https (SoupURI *uri, char **aliases)
+{
+ int i;
+
+ if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
+ return TRUE;
+ else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
+ return FALSE;
+ else if (!aliases)
+ return FALSE;
+
+ for (i = 0; aliases[i]; i++) {
+ if (uri->scheme == aliases[i])
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
G_DEFINE_BOXED_TYPE (SoupURI, soup_uri, soup_uri_copy, soup_uri_free)
diff --git a/libsoup/soup-uri.h b/libsoup/soup-uri.h
index b851dbec..fca97f76 100644
--- a/libsoup/soup-uri.h
+++ b/libsoup/soup-uri.h
@@ -31,14 +31,15 @@ struct _SoupURI {
GType soup_uri_get_type (void);
#define SOUP_TYPE_URI (soup_uri_get_type ())
-#define SOUP_URI_SCHEME_HTTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTP, "http")
-#define SOUP_URI_SCHEME_HTTPS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTPS, "https")
-#define SOUP_URI_SCHEME_FTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FTP, "ftp")
-#define SOUP_URI_SCHEME_FILE _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FILE, "file")
-#define SOUP_URI_SCHEME_DATA _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_DATA, "data")
+#define SOUP_URI_SCHEME_HTTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTP, "http")
+#define SOUP_URI_SCHEME_HTTPS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTPS, "https")
+#define SOUP_URI_SCHEME_FTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FTP, "ftp")
+#define SOUP_URI_SCHEME_FILE _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_FILE, "file")
+#define SOUP_URI_SCHEME_DATA _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_DATA, "data")
+#define SOUP_URI_SCHEME_RESOURCE _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_RESOURCE, "resource")
extern gpointer _SOUP_URI_SCHEME_HTTP, _SOUP_URI_SCHEME_HTTPS;
extern gpointer _SOUP_URI_SCHEME_FTP;
-extern gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA;
+extern gpointer _SOUP_URI_SCHEME_FILE, _SOUP_URI_SCHEME_DATA, _SOUP_URI_SCHEME_RESOURCE;
SoupURI *soup_uri_new_with_base (SoupURI *base,
const char *uri_string);
@@ -62,24 +63,31 @@ char *soup_uri_normalize (const char *part,
gboolean soup_uri_uses_default_port (SoupURI *uri);
+SOUP_AVAILABLE_IN_2_32
const char *soup_uri_get_scheme (SoupURI *uri);
void soup_uri_set_scheme (SoupURI *uri,
const char *scheme);
+SOUP_AVAILABLE_IN_2_32
const char *soup_uri_get_user (SoupURI *uri);
void soup_uri_set_user (SoupURI *uri,
const char *user);
+SOUP_AVAILABLE_IN_2_32
const char *soup_uri_get_password (SoupURI *uri);
void soup_uri_set_password (SoupURI *uri,
const char *password);
+SOUP_AVAILABLE_IN_2_32
const char *soup_uri_get_host (SoupURI *uri);
void soup_uri_set_host (SoupURI *uri,
const char *host);
+SOUP_AVAILABLE_IN_2_32
guint soup_uri_get_port (SoupURI *uri);
void soup_uri_set_port (SoupURI *uri,
guint port);
+SOUP_AVAILABLE_IN_2_32
const char *soup_uri_get_path (SoupURI *uri);
void soup_uri_set_path (SoupURI *uri,
const char *path);
+SOUP_AVAILABLE_IN_2_32
const char *soup_uri_get_query (SoupURI *uri);
void soup_uri_set_query (SoupURI *uri,
const char *query);
@@ -88,12 +96,16 @@ void soup_uri_set_query_from_form (SoupURI *uri,
void soup_uri_set_query_from_fields (SoupURI *uri,
const char *first_field,
...) G_GNUC_NULL_TERMINATED;
+SOUP_AVAILABLE_IN_2_32
const char *soup_uri_get_fragment (SoupURI *uri);
void soup_uri_set_fragment (SoupURI *uri,
const char *fragment);
+SOUP_AVAILABLE_IN_2_28
SoupURI *soup_uri_copy_host (SoupURI *uri);
+SOUP_AVAILABLE_IN_2_28
guint soup_uri_host_hash (gconstpointer key);
+SOUP_AVAILABLE_IN_2_28
gboolean soup_uri_host_equal (gconstpointer v1,
gconstpointer v2);
diff --git a/libsoup/soup-value-utils.c b/libsoup/soup-value-utils.c
index b929544b..e1f7c92f 100644
--- a/libsoup/soup-value-utils.c
+++ b/libsoup/soup-value-utils.c
@@ -15,7 +15,7 @@
/**
* SECTION:soup-value-utils
- * @short_description: #GValue utilities
+ * @short_description: GValue utilities
*
* These methods are useful for manipulating #GValue<!-- -->s, and in
* particular, arrays and hash tables of #GValue<!-- -->s, in a
diff --git a/libsoup/soup-version.c b/libsoup/soup-version.c
new file mode 100644
index 00000000..31e7262d
--- /dev/null
+++ b/libsoup/soup-version.c
@@ -0,0 +1,307 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-version.c: Version information
+ *
+ * Copyright (C) 2012 Igalia S.L.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-version.h"
+
+/**
+ * SECTION:soup-version
+ * @short_description: Variables and functions to check the libsoup version
+ **/
+
+/**
+ * SOUP_MAJOR_VERSION:
+ *
+ * Like soup_get_major_version(), but from the headers used at
+ * application compile time, rather than from the library linked
+ * against at application run time.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_MINOR_VERSION:
+ *
+ * Like soup_get_minor_version(), but from the headers used at
+ * application compile time, rather than from the library linked
+ * against at application run time.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_MICRO_VERSION:
+ *
+ * Like soup_get_micro_version(), but from the headers used at
+ * application compile time, rather than from the library linked
+ * against at application run time.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_CHECK_VERSION:
+ * @major: major version (e.g. 2 for version 2.42.0)
+ * @minor: minor version (e.g. 42 for version 2.42.0)
+ * @micro: micro version (e.g. 0 for version 2.42.0)
+ *
+ * Macro to test the version of libsoup being compiled against.
+ *
+ * Returns: %TRUE if the version of the libsoup header files
+ * is the same as or newer than the passed-in version.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * soup_get_major_version:
+ *
+ * Returns the major version number of the libsoup library.
+ * (e.g. in libsoup version 2.42.0 this is 2.)
+ *
+ * This function is in the library, so it represents the libsoup library
+ * your code is running against. Contrast with the #SOUP_MAJOR_VERSION
+ * macro, which represents the major version of the libsoup headers you
+ * have included when compiling your code.
+ *
+ * Returns: the major version number of the libsoup library
+ *
+ * Since: 2.42
+ */
+guint
+soup_get_major_version (void)
+{
+ return SOUP_MAJOR_VERSION;
+}
+
+/**
+ * soup_get_minor_version:
+ *
+ * Returns the minor version number of the libsoup library.
+ * (e.g. in libsoup version 2.42.0 this is 42.)
+ *
+ * This function is in the library, so it represents the libsoup library
+ * your code is running against. Contrast with the #SOUP_MINOR_VERSION
+ * macro, which represents the minor version of the libsoup headers you
+ * have included when compiling your code.
+ *
+ * Returns: the minor version number of the libsoup library
+ *
+ * Since: 2.42
+ */
+guint
+soup_get_minor_version (void)
+{
+ return SOUP_MINOR_VERSION;
+}
+
+/**
+ * soup_get_micro_version:
+ *
+ * Returns the micro version number of the libsoup library.
+ * (e.g. in libsoup version 2.42.0 this is 0.)
+ *
+ * This function is in the library, so it represents the libsoup library
+ * your code is running against. Contrast with the #SOUP_MICRO_VERSION
+ * macro, which represents the micro version of the libsoup headers you
+ * have included when compiling your code.
+ *
+ * Returns: the micro version number of the libsoup library
+ *
+ * Since: 2.42
+ */
+guint
+soup_get_micro_version (void)
+{
+ return SOUP_MICRO_VERSION;
+}
+
+/**
+ * soup_check_version:
+ * @major: the major version to check
+ * @minor: the minor version to check
+ * @micro: the micro version to check
+ *
+ * Like SOUP_CHECK_VERSION, but the check for soup_check_version is
+ * at runtime instead of compile time. This is useful for compiling
+ * against older versions of libsoup, but using features from newer
+ * versions.
+ *
+ * Returns: %TRUE if the version of the libsoup currently loaded
+ * is the same as or newer than the passed-in version.
+ *
+ * Since: 2.42
+ */
+gboolean
+soup_check_version (guint major,
+ guint minor,
+ guint micro)
+{
+ return SOUP_CHECK_VERSION (major, minor, micro);
+}
+
+/**
+ * SOUP_VERSION_MIN_REQUIRED:
+ *
+ * A macro that should be defined by the user prior to including
+ * libsoup.h. The definition should be one of the predefined libsoup
+ * version macros: %SOUP_VERSION_2_24, %SOUP_VERSION_2_26, ...
+ *
+ * This macro defines the earliest version of libsoup that the package
+ * is required to be able to compile against.
+ *
+ * If the compiler is configured to warn about the use of deprecated
+ * functions, then using functions that were deprecated in version
+ * %SOUP_VERSION_MIN_REQUIRED or earlier will cause warnings (but
+ * using functions deprecated in later releases will not).
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_MAX_ALLOWED:
+ *
+ * A macro that should be defined by the user prior to including
+ * libsoup.h. The definition should be one of the predefined libsoup
+ * version macros: %SOUP_VERSION_2_24, %SOUP_VERSION_2_26, ...
+ *
+ * This macro defines the latest version of the libsoup API that the
+ * package is allowed to make use of.
+ *
+ * If the compiler is configured to warn about the use of deprecated
+ * functions, then using functions added after version
+ * %SOUP_VERSION_MAX_ALLOWED will cause warnings.
+ *
+ * Unless you are using SOUP_CHECK_VERSION() or the like to compile
+ * different code depending on the libsoup version, then this should be
+ * set to the same value as %SOUP_VERSION_MIN_REQUIRED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_24:
+ *
+ * A macro that evaluates to the 2.24 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_26:
+ *
+ * A macro that evaluates to the 2.26 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_28:
+ *
+ * A macro that evaluates to the 2.28 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_30:
+ *
+ * A macro that evaluates to the 2.30 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_32:
+ *
+ * A macro that evaluates to the 2.32 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_34:
+ *
+ * A macro that evaluates to the 2.34 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_36:
+ *
+ * A macro that evaluates to the 2.36 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_38:
+ *
+ * A macro that evaluates to the 2.38 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_40:
+ *
+ * A macro that evaluates to the 2.40 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_42:
+ *
+ * A macro that evaluates to the 2.42 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.42
+ */
+
+/**
+ * SOUP_VERSION_2_44:
+ *
+ * A macro that evaluates to the 2.44 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.44
+ */
+
+/**
+ * SOUP_VERSION_2_46:
+ *
+ * A macro that evaluates to the 2.46 version of libsoup, in a format
+ * that can be used by %SOUP_VERSION_MIN_REQUIRED and
+ * %SOUP_VERSION_MAX_ALLOWED.
+ *
+ * Since: 2.46
+ */
diff --git a/libsoup/soup-version.h.in b/libsoup/soup-version.h.in
new file mode 100644
index 00000000..000b9456
--- /dev/null
+++ b/libsoup/soup-version.h.in
@@ -0,0 +1,261 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-version.h: Version information
+ *
+ * Copyright (C) 2012 Igalia S.L.
+ */
+
+#ifndef SOUP_VERSION_H
+#define SOUP_VERSION_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_MAJOR_VERSION (@SOUP_MAJOR_VERSION@)
+#define SOUP_MINOR_VERSION (@SOUP_MINOR_VERSION@)
+#define SOUP_MICRO_VERSION (@SOUP_MICRO_VERSION@)
+
+#define SOUP_CHECK_VERSION(major, minor, micro) \
+ (SOUP_MAJOR_VERSION > (major) || \
+ (SOUP_MAJOR_VERSION == (major) && SOUP_MINOR_VERSION > (minor)) || \
+ (SOUP_MAJOR_VERSION == (major) && SOUP_MINOR_VERSION == (minor) && \
+ SOUP_MICRO_VERSION >= (micro)))
+
+guint soup_get_major_version (void);
+
+guint soup_get_minor_version (void);
+
+guint soup_get_micro_version (void);
+
+gboolean soup_check_version (guint major,
+ guint minor,
+ guint micro);
+
+/* Deprecation / Availability macros */
+
+#define SOUP_ENCODE_VERSION(major,minor) ((major) << 16 | (minor) << 8)
+
+#define SOUP_VERSION_2_24 (SOUP_ENCODE_VERSION (2, 24))
+#define SOUP_VERSION_2_26 (SOUP_ENCODE_VERSION (2, 26))
+#define SOUP_VERSION_2_28 (SOUP_ENCODE_VERSION (2, 28))
+#define SOUP_VERSION_2_30 (SOUP_ENCODE_VERSION (2, 30))
+#define SOUP_VERSION_2_32 (SOUP_ENCODE_VERSION (2, 32))
+#define SOUP_VERSION_2_34 (SOUP_ENCODE_VERSION (2, 34))
+#define SOUP_VERSION_2_36 (SOUP_ENCODE_VERSION (2, 36))
+#define SOUP_VERSION_2_38 (SOUP_ENCODE_VERSION (2, 38))
+#define SOUP_VERSION_2_40 (SOUP_ENCODE_VERSION (2, 40))
+#define SOUP_VERSION_2_42 (SOUP_ENCODE_VERSION (2, 42))
+#define SOUP_VERSION_2_44 (SOUP_ENCODE_VERSION (2, 44))
+#define SOUP_VERSION_2_46 (SOUP_ENCODE_VERSION (2, 46))
+
+/* evaluates to the current stable version; for development cycles,
+ * this means the next stable target
+ */
+#if (SOUP_MINOR_VERSION % 2)
+#define SOUP_VERSION_CUR_STABLE (SOUP_ENCODE_VERSION (SOUP_MAJOR_VERSION, SOUP_MINOR_VERSION + 1))
+#else
+#define SOUP_VERSION_CUR_STABLE (SOUP_ENCODE_VERSION (SOUP_MAJOR_VERSION, SOUP_MINOR_VERSION))
+#endif
+
+/* evaluates to the previous stable version */
+#if (SOUP_MINOR_VERSION % 2)
+#define SOUP_VERSION_PREV_STABLE (SOUP_ENCODE_VERSION (SOUP_MAJOR_VERSION, SOUP_MINOR_VERSION - 1))
+#else
+#define SOUP_VERSION_PREV_STABLE (SOUP_ENCODE_VERSION (SOUP_MAJOR_VERSION, SOUP_MINOR_VERSION - 2))
+#endif
+
+#ifndef SOUP_VERSION_MIN_REQUIRED
+# define SOUP_VERSION_MIN_REQUIRED (SOUP_VERSION_CUR_STABLE)
+#elif SOUP_VERSION_MIN_REQUIRED == 0
+# undef SOUP_VERSION_MIN_REQUIRED
+# define SOUP_VERSION_MIN_REQUIRED (SOUP_VERSION_CUR_STABLE + 2)
+#endif
+
+#if !defined (SOUP_VERSION_MAX_ALLOWED) || (SOUP_VERSION_MAX_ALLOWED == 0)
+# undef SOUP_VERSION_MAX_ALLOWED
+# define SOUP_VERSION_MAX_ALLOWED (SOUP_VERSION_CUR_STABLE)
+#endif
+
+/* sanity checks */
+#if SOUP_VERSION_MIN_REQUIRED > SOUP_VERSION_CUR_STABLE
+#error "SOUP_VERSION_MIN_REQUIRED must be <= SOUP_VERSION_CUR_STABLE"
+#endif
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_MIN_REQUIRED
+#error "SOUP_VERSION_MAX_ALLOWED must be >= SOUP_VERSION_MIN_REQUIRED"
+#endif
+#if SOUP_VERSION_MIN_REQUIRED < SOUP_VERSION_2_24
+#error "SOUP_VERSION_MIN_REQUIRED must be >= SOUP_VERSION_2_24"
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_24
+# define SOUP_DEPRECATED_IN_2_24 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_24_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_24
+# define SOUP_DEPRECATED_IN_2_24_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_24
+# define SOUP_AVAILABLE_IN_2_24 GLIB_UNAVAILABLE(2, 24)
+#else
+# define SOUP_AVAILABLE_IN_2_24
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_26
+# define SOUP_DEPRECATED_IN_2_26 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_26_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_26
+# define SOUP_DEPRECATED_IN_2_26_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_26
+# define SOUP_AVAILABLE_IN_2_26 GLIB_UNAVAILABLE(2, 26)
+#else
+# define SOUP_AVAILABLE_IN_2_26
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_28
+# define SOUP_DEPRECATED_IN_2_28 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_28_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_28
+# define SOUP_DEPRECATED_IN_2_28_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_28
+# define SOUP_AVAILABLE_IN_2_28 GLIB_UNAVAILABLE(2, 28)
+#else
+# define SOUP_AVAILABLE_IN_2_28
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_30
+# define SOUP_DEPRECATED_IN_2_30 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_30_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_30
+# define SOUP_DEPRECATED_IN_2_30_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_30
+# define SOUP_AVAILABLE_IN_2_30 GLIB_UNAVAILABLE(2, 30)
+#else
+# define SOUP_AVAILABLE_IN_2_30
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_32
+# define SOUP_DEPRECATED_IN_2_32 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_32_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_32
+# define SOUP_DEPRECATED_IN_2_32_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_32
+# define SOUP_AVAILABLE_IN_2_32 GLIB_UNAVAILABLE(2, 32)
+#else
+# define SOUP_AVAILABLE_IN_2_32
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_34
+# define SOUP_DEPRECATED_IN_2_34 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_34_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_34
+# define SOUP_DEPRECATED_IN_2_34_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_34
+# define SOUP_AVAILABLE_IN_2_34 GLIB_UNAVAILABLE(2, 34)
+#else
+# define SOUP_AVAILABLE_IN_2_34
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_36
+# define SOUP_DEPRECATED_IN_2_36 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_36_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_36
+# define SOUP_DEPRECATED_IN_2_36_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_36
+# define SOUP_AVAILABLE_IN_2_36 GLIB_UNAVAILABLE(2, 36)
+#else
+# define SOUP_AVAILABLE_IN_2_36
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_38
+# define SOUP_DEPRECATED_IN_2_38 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_38_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_38
+# define SOUP_DEPRECATED_IN_2_38_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_38
+# define SOUP_AVAILABLE_IN_2_38 GLIB_UNAVAILABLE(2, 38)
+#else
+# define SOUP_AVAILABLE_IN_2_38
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_40
+# define SOUP_DEPRECATED_IN_2_40 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_40_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_40
+# define SOUP_DEPRECATED_IN_2_40_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_40
+# define SOUP_AVAILABLE_IN_2_40 GLIB_UNAVAILABLE(2, 40)
+#else
+# define SOUP_AVAILABLE_IN_2_40
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_42
+# define SOUP_DEPRECATED_IN_2_42 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_42_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_42
+# define SOUP_DEPRECATED_IN_2_42_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_42
+# define SOUP_AVAILABLE_IN_2_42 GLIB_UNAVAILABLE(2, 42)
+#else
+# define SOUP_AVAILABLE_IN_2_42
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_44
+# define SOUP_DEPRECATED_IN_2_44 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_44_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_44
+# define SOUP_DEPRECATED_IN_2_44_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_44
+# define SOUP_AVAILABLE_IN_2_44 GLIB_UNAVAILABLE(2, 44)
+#else
+# define SOUP_AVAILABLE_IN_2_44
+#endif
+
+#if SOUP_VERSION_MIN_REQUIRED >= SOUP_VERSION_2_46
+# define SOUP_DEPRECATED_IN_2_46 GLIB_DEPRECATED
+# define SOUP_DEPRECATED_IN_2_46_FOR(f) GLIB_DEPRECATED_FOR(f)
+#else
+# define SOUP_DEPRECATED_IN_2_46
+# define SOUP_DEPRECATED_IN_2_46_FOR(f)
+#endif
+
+#if SOUP_VERSION_MAX_ALLOWED < SOUP_VERSION_2_46
+# define SOUP_AVAILABLE_IN_2_46 GLIB_UNAVAILABLE(2, 46)
+#else
+# define SOUP_AVAILABLE_IN_2_46
+#endif
+
+G_END_DECLS
+
+#endif /* SOUP_VERSION_H */
diff --git a/libsoup/soup-xmlrpc.c b/libsoup/soup-xmlrpc.c
index f7908585..e2890100 100644
--- a/libsoup/soup-xmlrpc.c
+++ b/libsoup/soup-xmlrpc.c
@@ -290,6 +290,11 @@ soup_xmlrpc_build_method_response (GValue *value)
}
static char *
+soup_xmlrpc_build_faultv (int fault_code,
+ const char *fault_format,
+ va_list args) G_GNUC_PRINTF (2, 0);
+
+static char *
soup_xmlrpc_build_faultv (int fault_code, const char *fault_format, va_list args)
{
xmlDoc *doc;
@@ -593,29 +598,32 @@ soup_xmlrpc_parse_method_call (const char *method_call, int length,
xmlMethodName = xmlNodeGetContent (node);
node = find_real_node (node->next);
- if (!node || strcmp ((const char *)node->name, "params") != 0)
- goto fail;
+ if (node) {
+ if (strcmp ((const char *)node->name, "params") != 0)
+ goto fail;
#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
#endif
- *params = g_value_array_new (1);
- param = find_real_node (node->children);
- while (param && !strcmp ((const char *)param->name, "param")) {
- xval = find_real_node (param->children);
- if (!xval || strcmp ((const char *)xval->name, "value") != 0 ||
- !parse_value (xval, &value)) {
- g_value_array_free (*params);
- goto fail;
- }
- g_value_array_append (*params, &value);
- g_value_unset (&value);
+ *params = soup_value_array_new ();
+ param = find_real_node (node->children);
+ while (param && !strcmp ((const char *)param->name, "param")) {
+ xval = find_real_node (param->children);
+ if (!xval || strcmp ((const char *)xval->name, "value") != 0 ||
+ !parse_value (xval, &value)) {
+ g_value_array_free (*params);
+ goto fail;
+ }
+ g_value_array_append (*params, &value);
+ g_value_unset (&value);
- param = find_real_node (param->next);
- }
+ param = find_real_node (param->next);
+ }
#ifdef G_GNUC_END_IGNORE_DEPRECATIONS
G_GNUC_END_IGNORE_DEPRECATIONS
#endif
+ } else
+ *params = soup_value_array_new ();
success = TRUE;
*method_name = g_strdup ((char *)xmlMethodName);
diff --git a/libsoup/soup.h b/libsoup/soup.h
index d4ddcead..82a26329 100644
--- a/libsoup/soup.h
+++ b/libsoup/soup.h
@@ -15,10 +15,13 @@ extern "C" {
#include <libsoup/soup-auth-domain.h>
#include <libsoup/soup-auth-domain-basic.h>
#include <libsoup/soup-auth-domain-digest.h>
+#include <libsoup/soup-auth-manager.h>
+#include <libsoup/soup-cache.h>
#include <libsoup/soup-content-decoder.h>
#include <libsoup/soup-content-sniffer.h>
#include <libsoup/soup-cookie.h>
#include <libsoup/soup-cookie-jar.h>
+#include <libsoup/soup-cookie-jar-db.h>
#include <libsoup/soup-cookie-jar-text.h>
#include <libsoup/soup-date.h>
#include <libsoup/soup-enum-types.h>
@@ -29,10 +32,14 @@ extern "C" {
#include <libsoup/soup-method.h>
#include <libsoup/soup-misc.h>
#include <libsoup/soup-multipart.h>
-#include <libsoup/soup-password-manager.h>
+#include <libsoup/soup-multipart-input-stream.h>
#include <libsoup/soup-proxy-resolver.h>
#include <libsoup/soup-proxy-resolver-default.h>
#include <libsoup/soup-proxy-uri-resolver.h>
+#include <libsoup/soup-request.h>
+#include <libsoup/soup-request-data.h>
+#include <libsoup/soup-request-file.h>
+#include <libsoup/soup-request-http.h>
#include <libsoup/soup-server.h>
#include <libsoup/soup-session-async.h>
#include <libsoup/soup-session-feature.h>
@@ -42,6 +49,7 @@ extern "C" {
#include <libsoup/soup-tld.h>
#include <libsoup/soup-uri.h>
#include <libsoup/soup-value-utils.h>
+#include <libsoup/soup-version.h>
#include <libsoup/soup-xmlrpc.h>
#ifdef __cplusplus
diff --git a/libsoup/tld-parser.py b/libsoup/tld-parser.py
index c1a0346f..5d9d2ba5 100755
--- a/libsoup/tld-parser.py
+++ b/libsoup/tld-parser.py
@@ -5,13 +5,14 @@
# Based on tld-parser.c Copyright (C) 2012 Igalia S.L.
import sys
+import codecs
SOUP_TLD_RULE_NORMAL = 0
SOUP_TLD_RULE_MATCH_ALL = 1 << 0
SOUP_TLD_RULE_EXCEPTION = 1 << 1
-tlds_file = open(sys.argv[1])
-inc_file = open(sys.argv[2], 'w')
+tlds_file = codecs.open(sys.argv[1], encoding='utf-8')
+inc_file = codecs.open(sys.argv[2], 'w', encoding='utf-8')
first = True
for rule in tlds_file:
diff --git a/m4/glibtests.m4 b/m4/glibtests.m4
new file mode 100644
index 00000000..7d5920a4
--- /dev/null
+++ b/m4/glibtests.m4
@@ -0,0 +1,28 @@
+dnl GLIB_TESTS
+dnl
+
+AC_DEFUN([GLIB_TESTS],
+[
+ AC_ARG_ENABLE(installed-tests,
+ AS_HELP_STRING([--enable-installed-tests],
+ [Enable installation of some test cases]),
+ [case ${enableval} in
+ yes) ENABLE_INSTALLED_TESTS="1" ;;
+ no) ENABLE_INSTALLED_TESTS="" ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-installed-tests]) ;;
+ esac])
+ AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], test "$ENABLE_INSTALLED_TESTS" = "1")
+ AC_ARG_ENABLE(always-build-tests,
+ AS_HELP_STRING([--enable-always-build-tests],
+ [Enable always building tests during 'make all']),
+ [case ${enableval} in
+ yes) ENABLE_ALWAYS_BUILD_TESTS="1" ;;
+ no) ENABLE_ALWAYS_BUILD_TESTS="" ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-always-build-tests]) ;;
+ esac])
+ AM_CONDITIONAL([ENABLE_ALWAYS_BUILD_TESTS], test "$ENABLE_ALWAYS_BUILD_TESTS" = "1")
+ if test "$ENABLE_INSTALLED_TESTS" = "1"; then
+ AC_SUBST(installed_test_metadir, [${datadir}/installed-tests/]AC_PACKAGE_NAME)
+ AC_SUBST(installed_testdir, [${libexecdir}/installed-tests/]AC_PACKAGE_NAME)
+ fi
+])
diff --git a/po/LINGUAS b/po/LINGUAS
index fc8a7380..a1d98a9e 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -1,22 +1,58 @@
+an
as
-bg
be
+bg
+bn_IN
+ca
+ca@valencia
+cs
+da
+de
el
+en_GB
+eo
es
+et
+eu
+fa
fr
+fur
gl
+gu
he
+hi
+hu
id
+it
+ja
+kn
+ko
lt
+lv
+ml
+mr
nb
+nl
+or
pa
pl
+pt
+pt_BR
ro
ru
+sk
sl
sr
sr@latin
+sv
+ta
te
+tg
+th
+tr
+ug
+uk
+uz@cyrillic
vi
zh_CN
zh_HK
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a05aa79b..21c70d42 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,6 +1,9 @@
libsoup/soup-body-input-stream.c
+libsoup/soup-cache-input-stream.c
libsoup/soup-converter-wrapper.c
+libsoup/soup-message-client-io.c
libsoup/soup-message-io.c
+libsoup/soup-message-server-io.c
libsoup/soup-request.c
-libsoup/soup-requester.c
+libsoup/soup-session.c
libsoup/soup-tld.c
diff --git a/po/an.po b/po/an.po
new file mode 100644
index 00000000..5d207a9a
--- /dev/null
+++ b/po/an.po
@@ -0,0 +1,108 @@
+# Aragonese translation for libsoup.
+# Copyright (C) 2013 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-19 15:37+0000\n"
+"PO-Revision-Date: 2013-02-19 23:17+0100\n"
+"Last-Translator: Daniel Martinez <entaltoaragon@gmail.com>\n"
+"Language-Team: Aragonese <softaragones@googlegroups.com>\n"
+"Language: an\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205
+#: ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "A connexión remató inasperadament"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Petición de búsqueda no válida"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "No se puede truncar SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "O fluxo de ret se zarró inasperadament"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Falló en cachear completament o recurso"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "O búfer de salida ye masiau chicot"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "No se podió analisar a respuesta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Codificación d'a respuesta HTTP no reconoixida"
+
+#: ../libsoup/soup-message-io.c:846
+#: ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Se canceló a operación"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "A operación se blocará"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "No se podió analisar a solicitut HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "No s'ha proporcionau un URI"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "URI '%s' no válida: %s"
+
+#: ../libsoup/soup-session.c:4211
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "No se podió analisar l'URI «%s»"
+
+#: ../libsoup/soup-session.c:4248
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Esquema %s d'URI no suportau"
+
+#: ../libsoup/soup-session.c:4270
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "No ye un URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "O nombre de l'equipo ye una adreza IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "O nombre de l'equipo no ye válido"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "O nombre de l'equipo no tiene un dominio base"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "No i hai suficients dominios"
+
diff --git a/po/as.po b/po/as.po
index 442b9813..b87688a1 100644
--- a/po/as.po
+++ b/po/as.po
@@ -2,71 +2,108 @@
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
#
-# Nilamdyuti Goswami <ngoswami@redhat.com>, 2012.
+# Nilamdyuti Goswami <ngoswami@redhat.com>, 2012, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
-"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug."
-"cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-07-12 15:59+0000\n"
-"PO-Revision-Date: 2012-07-13 20:11+0530\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-12 18:37+0530\n"
"Last-Translator: Nilamdyuti Goswami <ngoswami@redhat.com>\n"
-"Language-Team: as_IN <kde-i18n-doc@kde.org>\n"
+"Language-Team: Assamese <kde-i18n-doc@kde.org>\n"
"Language: as\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Lokalize 1.0\n"
+"X-Generator: Lokalize 1.5\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "সংযোগ অপ্ৰত্যাশিতভাৱে অন্ত হল"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "অবৈধ সন্ধান অনুৰোধ"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream চুটি কৰিব নোৱাৰি"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "নেটৱাৰ্ক স্ট্ৰিম অপ্ৰত্যাশিতভাৱে বন্ধ হল"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "সম্পদক সম্পূৰ্ণভাৱে ক্যাশ কৰিবলে ব্যৰ্থ"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "আউটপুট বাফাৰ অতি সৰু"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "HTTP প্ৰতিক্ৰিয়া বিশ্লেষণ কৰিব পৰা নগল"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "অপৰিচিত HTTP প্ৰতিক্ৰিয়া এনক'ডিং"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "কাৰ্য্য বাতিল কৰা হৈছিল"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "কাৰ্য্য প্ৰতিৰোধ কৰিব"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "HTTP অনুৰোধ বিশ্লেষণ কৰিব পৰা নগল"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "কোনো URl প্ৰদান কৰা হোৱা নাই"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "অবৈধ '%s' URI: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "URI '%s' বিশ্লেষণ কৰিব পৰা নগল"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "অসমৰ্থিত URl আঁচনি '%s'"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "এটা HTTP URI নহয়"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "হস্টনাম এটা IP ঠিকনা"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "অবৈধ হস্টনাম"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "হস্টনামৰ কোনো ভিত্তি ডমেইন নাই"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "পৰ্যাপ্ত ডমেইন নাই"
diff --git a/po/be.po b/po/be.po
index 53bcfe54..e2b35341 100644
--- a/po/be.po
+++ b/po/be.po
@@ -5,62 +5,99 @@ msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-06-14 18:14+0000\n"
-"PO-Revision-Date: 2012-07-15 14:23+0300\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2012-09-14 13:29+0300\n"
"Last-Translator: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>\n"
"Language-Team: Belarusian <i18n-bel-gnome@googlegroups.com>\n"
+"Language: be\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: be\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Злучэнне нечакана перарвана"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Хібны запыт пракручвання змесціва"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Немагчыма абрэзаць SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Сеткавы струмень нечакана закрыўся"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Не ўдалося цалкам змясціць рэсурс у кэш-памяці"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Выхадны буфер надта малы"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Не ўдалося разабраць HTTP-адказ"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Невядомае кадаванне HTTP-адказу"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Аперацыя была скасавана"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Аперацыя заблакіруе працэс"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Не ўдалося разабраць HTTP-запыт"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "URI-адрас не пададзены"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Хібны URI-адрас \"%s\": %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Не ўдалося разабраць URI-адрас \"%s\""
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "URI-схема \"%s\" не падтрымліваецца"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Гэта не HTTP URI-адрас"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Назва хоста з'яўляецца IP-адрасам"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Хібная назва хоста"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Назва хоста не мае базавага дамена"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Не хапае даменаў"
diff --git a/po/bg.po b/po/bg.po
index 7781f397..42ae49e2 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,14 +1,14 @@
# Bulgarian translation of libsoup po-file.
-# Copyright (C) 2012 Free Software Foundation, Inc.
+# Copyright (C) 2012, 2013 Free Software Foundation, Inc.
# This file is distributed under the same license as the libsoup package.
-# Alexander Shopov <ash@kambanaria.org>, 2012.
+# Alexander Shopov <ash@kambanaria.org>, 2012, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-05 12:37+0300\n"
-"PO-Revision-Date: 2012-07-05 12:37+0300\n"
+"POT-Creation-Date: 2013-01-12 06:56+0200\n"
+"PO-Revision-Date: 2013-01-12 06:56+0200\n"
"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
"Language: \n"
@@ -17,53 +17,86 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Връзката прекъсна неочаквано"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:461
+msgid "Invalid seek request"
+msgstr "Неправилна заявка за търсене"
+
+#: ../libsoup/soup-body-input-stream.c:489
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Потокът SoupBodyInputStream не може да бъде прекъснат"
+
+#: ../libsoup/soup-cache-input-stream.c:77
+msgid "Network stream unexpectedly closed"
+msgstr "Потокът от мрежата неочаквано прекъсна"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Прекалено малък изходен буфер"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Отговорът от HTTP не може да бъде анализиран"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Непознато кодиране на отговора от HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Операцията е отменена"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Операцията ще блокира"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Заявката към HTTP не може да бъде анализирана"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Не е даден адрес"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Неправилен адрес на „%s“: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4032
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Адресът „%s“ е неправилен"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4069
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Схемата на адреса не се поддържа „%s“"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4091
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Не е адрес URI за HTTP"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Името на машината е адрес по IP"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Неправилно име на машина"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Името на машината не съдържа домейн"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Няма достатъчно домейни"
diff --git a/po/bn_IN.po b/po/bn_IN.po
new file mode 100644
index 00000000..847d4b0d
--- /dev/null
+++ b/po/bn_IN.po
@@ -0,0 +1,83 @@
+# Bengali (India) translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# Sayak Sarkar <sayak.bugsmith@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-09-20 11:30+0000\n"
+"PO-Revision-Date: 2012-09-20 22:38+0530\n"
+"Last-Translator: Sayak Sarkar <sayak.bugsmith@gmail.com>\n"
+"Language-Team: Bengali (India) <anubad@lists.ankur.org.in>\n"
+"Language: bn\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.4\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "সংযোগ অপ্রত্যাশিতভাবে পর্যবসিত"
+
+#: ../libsoup/soup-body-input-stream.c:461
+msgid "Invalid seek request"
+msgstr "অবৈধ সিক্ অনুরোধ"
+
+#: ../libsoup/soup-body-input-stream.c:489
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream ছাঁটা যাচ্ছে না।"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "আউটপুট বাফার অত্যন্ত ছোট"
+
+#: ../libsoup/soup-message-io.c:818 ../libsoup/soup-message-io.c:854
+msgid "Operation was cancelled"
+msgstr "অপারেশন বাতিল হয়েছে"
+
+#: ../libsoup/soup-message-io.c:865
+msgid "Operation would block"
+msgstr "অপারেশন ব্লক করবে"
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "কোন URI দেওয়া হয়নি"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "অবৈধ '%s' URI: %s"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s' পার্স করা যায়নি"
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "অসমর্থিত URI স্কিম '%s'"
+
+#: ../libsoup/soup-tld.c:154
+msgid "Hostname is an IP address"
+msgstr "হোস্টনেমটি একটি IP অ্যাড্রেস"
+
+#: ../libsoup/soup-tld.c:175
+msgid "Invalid hostname"
+msgstr "অবৈধ হোস্টনেম"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "হোস্টনেমটির কোনো বেস ডোমেইন নেই"
+
+#: ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "ডোমেইনগুলি পর্যাপ্ত নয়"
diff --git a/po/ca.po b/po/ca.po
new file mode 100644
index 00000000..60d22f79
--- /dev/null
+++ b/po/ca.po
@@ -0,0 +1,108 @@
+# Catalan translation for libsoup.
+# Copyright (C) 2012 Free Software Foundation, Inc.
+# This file is distributed under the same license as the libsoup package.
+# Gil Forcada <gilforcada@guifi.net>, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-03-09 09:45+0000\n"
+"PO-Revision-Date: 2012-09-23 17:22+0200\n"
+"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
+"Language-Team: Catalan <tradgnome@softcatala.org>\n"
+"Language: ca\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bits\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Gtranslator 2.91.5\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "S'ha finalitzat la connexió inesperadament"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "La petició de cerca no és vàlida"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "No es pot truncar el SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "S'ha tancat inesperadament el flux de xarxa"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "No s'ha pogut carregar completament el recurs a la memòria cau"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "La memòria intermèdia de sortida és massa petita"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "No s'ha pogut analitzar la resposta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "No es reconeix la codificació de la resposta HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "S'ha cancel·lat l'operació"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "L'operació bloquejaria"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "No s'ha pogut analitzar la petició HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "No s'ha proporcionat cap URI"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "L'URI «%s» no és vàlid: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "No s'ha pogut analitzar l'URI «%s»"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "No es sap gestionar l'esquema d'URI «%s»"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "No és un URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "El nom d'ordinador és una adreça IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "El nom d'ordinador no és vàlid"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "El nom d'ordinador no té cap domini base"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "No hi ha prou dominis"
diff --git a/po/ca@valencia.po b/po/ca@valencia.po
new file mode 100644
index 00000000..04aee98f
--- /dev/null
+++ b/po/ca@valencia.po
@@ -0,0 +1,107 @@
+# Catalan translation for libsoup.
+# Copyright (C) 2012 Free Software Foundation, Inc.
+# This file is distributed under the same license as the libsoup package.
+# Gil Forcada <gilforcada@guifi.net>, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-03-12 23:07+0100\n"
+"PO-Revision-Date: 2012-09-23 17:22+0200\n"
+"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
+"Language-Team: Catalan <tradgnome@softcatala.org>\n"
+"Language: ca-XV\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bits\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Gtranslator 2.91.5\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "S'ha finalitzat la connexió inesperadament"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "La petició de cerca no és vàlida"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "No es pot truncar el SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "S'ha tancat inesperadament el flux de xarxa"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "No s'ha pogut carregar completament el recurs a la memòria cau"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "La memòria intermèdia d'eixida és massa petita"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "No s'ha pogut analitzar la resposta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "No es reconeix la codificació de la resposta HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "S'ha cancel·lat l'operació"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "L'operació bloquejaria"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "No s'ha pogut analitzar la petició HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "No s'ha proporcionat cap URI"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "L'URI «%s» no és vàlid: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "No s'ha pogut analitzar l'URI «%s»"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "No es sap gestionar l'esquema d'URI «%s»"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "No és un URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "El nom d'ordinador és una adreça IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "El nom d'ordinador no és vàlid"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "El nom d'ordinador no té cap domini base"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "No hi ha prou dominis"
diff --git a/po/cs.po b/po/cs.po
new file mode 100644
index 00000000..3deeec02
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,110 @@
+# Czech translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Marek Černocký <marek@manet.cz>, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-02-19 11:21+0100\n"
+"Last-Translator: Marek Černocký <marek@manet.cz>\n"
+"Language-Team: Czech <gnome-cs-list@gnome.org>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Gtranslator 2.91.6\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Připojení bylo neočekávaně ukončeno"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Neplatný požadavek na posun"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Nelze zkrátit SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Síťový proud byl neočekávaně uzavřen"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Selhalo úplné uložení prostředku do mezipaměti"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Výstupní vyrovnávací paměť je příliš malá"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "Nelze zpracovat odpověď HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Nerozpoznáno kódování odpovědi HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Operace byla zrušena"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Operace by blokovala"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "Nelze zpracovat požadavek HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Není poskytnuta žádná adresa URI"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Neplatná adresa URI „%s“: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Nelze zpracovat adresu URI „%s“"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Nepodporované schéma URI „%s“"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Nejedná se o adresu HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Název počítače je adresa IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Neplatný název počítače"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Název počítače nemá základní doménu"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Nedostatek domén"
diff --git a/po/da.po b/po/da.po
new file mode 100644
index 00000000..fc187292
--- /dev/null
+++ b/po/da.po
@@ -0,0 +1,108 @@
+# Danish translation for libsoup.
+# Copyright (C) 2013 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Ask Hjorth Larsen <asklarsen@gmail.com>, 2012.
+# Joe Hansen (joedalton2@yahoo.dk), 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-03-12 23:14+0100\n"
+"PO-Revision-Date: 2013-03-12 18:15+0200\n"
+"Last-Translator: Joe Hansen <joedalton2@yahoo.dk>\n"
+"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Forbindelsen blev uventet afbrudt"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Ugyldig søgeforespørgsel"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Kan ikke afkorte SoupBodyInputStream"
+
+# evt. Netværksstrømmen
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Netværksudsendelsen blev uventet lukket ned"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Kunne ikke lave fuldt mellemlager for ressourcen"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Outputbuffer er for lille"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Kunne ikke fortolke HTTP-svar"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Ej genkendt HTTP-svarkodning"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Operationen blev annulleret"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Operationen ville blokere"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Kunne ikke fortolke HTTP-forespørgsel"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Ingen URI givet"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Ugyldig \"%s\"-URI: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Kunne ikke fortolke URI \"%s\""
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Uunderstøttet URI-skema \"%s\""
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Ikke en HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Værtsnavn er en IP-adresse"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Ugyldigt værtsnavn"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Værtsnavnet har intet basisdomæne"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Ikke nok domæner"
diff --git a/po/de.po b/po/de.po
new file mode 100644
index 00000000..cdb26022
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,110 @@
+# German translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Tobias Endrigkeit <tobiasendrigkeit@googlemail.com>, 2012.
+# Mario Blättermann <mario.blaettermann@gmail.com>, 2012, 2013.
+# Andre Jonas <nipsky@googlemail.com>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-03 21:07+0100\n"
+"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
+"Language-Team: Deutsch <German <gnome-de@gnome.org>>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Gtranslator 2.91.6\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Die Verbindung wurde unerwartet beendet"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Ungültige Suchanfrage"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream konnte nicht abgeschnitten werden"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Netzwerk-Stream wurde unerwartet geschlossen"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Das vollständige Zwischenspeichern der Ressource ist fehlgeschlagen"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Der Ausgabe-Zwischenspeicher ist zu klein"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "HTTP-Antwort konnte nicht verarbeitet werden"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Unbekannte Kodierung der HTTP-Antwort"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Der Vorgang wurde abgebrochen"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Der Vorgang würde gestoppt werden"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "HTTP-Anfrage konnte nicht verarbeitet werden"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Keine Adresse wurde bereitgestellt"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Ungültige »%s« Adresse: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Die Adresse »%s« konnte nicht verarbeitet werden"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Nicht unterstütztes Adressenschema »%s«"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Keine HTTP-Adresse"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Der Rechnername ist eine IP-Adresse"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Ungültiger Rechnername"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Der Rechnername hat keine Hauptdomäne"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Nicht genug Domänen"
diff --git a/po/el.po b/po/el.po
index f693bc5e..d71f863d 100644
--- a/po/el.po
+++ b/po/el.po
@@ -2,71 +2,110 @@
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
# Tom Tryfonidis <tomtryf@gmail.com>, 2012.
-#
+# Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
-"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-06-14 18:14+0000\n"
-"PO-Revision-Date: 2012-06-22 13:16+0200\n"
-"Last-Translator: Tom Tryfonidis <tomtryf@gmail.com>\n"
-"Language-Team: Greek <team@lists.gnome.gr>\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsou"
+"p&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-09 11:44+0300\n"
+"Last-Translator: Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>\n"
+"Language-Team: team@gnome.gr\n"
+"Language: el\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
+"X-Project-Style: gnome\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203
-#: ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Η σύνδεση τερματίστηκε απρόσμενα"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Άκυρη αίτηση αναζήτησης"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Αδυναμία περικοπής του SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Η ροή του δικτύου έκλεισε αναπάντεχα"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Αποτυχία πλήρους απόκρυψης του πόρου"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
-msgstr "Το buffer εξόδου είναι πολύ μικρό"
+msgstr "Η ενδιάμεση μνήμη εξόδου είναι πολύ μικρή"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "Αδυναμία ανάλυσης της απάντησης HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Μη αναγνωρίσιμη κωδικοποίηση απάντησης HTTP"
-#: ../libsoup/soup-message-io.c:836
-#: ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Η λειτουργία ακυρώθηκε"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Η λειτουργία θα μπλοκαριστεί"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "Αδυναμία ανάλυσης αιτήματος HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Δεν έχει δοθεί URI"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Μη έγκυρο '%s' URI: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Αδυναμία ανάλυσης URI '%s'"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Μη υποστηριζόμενο URI σχήμα '%s'"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Δεν είναι URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Το όνομα συστήματος είναι μια διεύθυνση IP"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Μη έγκυρο όνομα συστήματος"
-#: ../libsoup/soup-tld.c:181
-#: ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Το όνομα συστήματος δεν έχει βασικό τομέα"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Δεν υπάρχουν αρκετοί τομείς"
-
diff --git a/po/en_GB.po b/po/en_GB.po
new file mode 100644
index 00000000..1331d1e7
--- /dev/null
+++ b/po/en_GB.po
@@ -0,0 +1,84 @@
+# British English translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Bruce Cowan <bruce@bcowan.me.uk>, 2012.
+# Chris Leonard <cjl@laptop.org>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-09-10 19:10+0000\n"
+"PO-Revision-Date: 2012-09-10 21:39-0400\n"
+"Last-Translator: Chris Leonard <cjl@laptop.org>\n"
+"Language-Team: Sugar Labs\n"
+"Language: en_GB\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.0\n"
+"X-Project-Style: gnome\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Connection terminated unexpectedly"
+
+#: ../libsoup/soup-body-input-stream.c:461
+msgid "Invalid seek request"
+msgstr "Invalid seek request"
+
+#: ../libsoup/soup-body-input-stream.c:489
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Cannot truncate SoupBodyInputStream"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Output buffer is too small"
+
+#: ../libsoup/soup-message-io.c:818 ../libsoup/soup-message-io.c:854
+msgid "Operation was cancelled"
+msgstr "Operation was cancelled"
+
+#: ../libsoup/soup-message-io.c:865
+msgid "Operation would block"
+msgstr "Operation would block"
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "No URI provided"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Invalid '%s' URI: %s"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Could not parse URI '%s'"
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Unsupported URI scheme '%s'"
+
+#: ../libsoup/soup-tld.c:154
+msgid "Hostname is an IP address"
+msgstr "Hostname is an IP address"
+
+#: ../libsoup/soup-tld.c:175
+msgid "Invalid hostname"
+msgstr "Invalid hostname"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "Hostname has no base domain"
+
+#: ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "Not enough domains"
diff --git a/po/eo.po b/po/eo.po
new file mode 100644
index 00000000..c1550eef
--- /dev/null
+++ b/po/eo.po
@@ -0,0 +1,74 @@
+# Esperanto translation for libsoup.
+# Copyright (C) 2012 Free Software Foundation, Inc.
+# This file is distributed under the same license as the libsoup package.
+# Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-07-31 00:03+0000\n"
+"PO-Revision-Date: 2012-08-01 19:57+0200\n"
+"Last-Translator: Kristjan SCHMIDT <kristjan.schmidt@googlemail.com>\n"
+"Language-Team: Esperanto <gnome-l10n-eo@lists.launchpad.net>\n"
+"Language: eo\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ../libsoup/soup-body-input-stream.c:136
+#: ../libsoup/soup-body-input-stream.c:167
+#: ../libsoup/soup-body-input-stream.c:200 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "La konekto estas neatendite fermita."
+
+#: ../libsoup/soup-converter-wrapper.c:191
+#, c-format
+msgid "Output buffer is too small"
+msgstr "La elig-bufro estas tro malgranda"
+
+#: ../libsoup/soup-message-io.c:817 ../libsoup/soup-message-io.c:853
+msgid "Operation was cancelled"
+msgstr "La operacio estas ĉesigita"
+
+#: ../libsoup/soup-message-io.c:864
+msgid "Operation would block"
+msgstr "La operacio estus haltigota."
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "Neniu URI estas diponigita"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Nevalida '%s' URI: %s"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Ne eblis analizi na URI '%s'"
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Nesuptenata URI-skemo '%s'"
+
+#: ../libsoup/soup-tld.c:148
+msgid "Hostname is an IP address"
+msgstr "La komputilnomo estas IP-adreso"
+
+#: ../libsoup/soup-tld.c:169
+msgid "Invalid hostname"
+msgstr "Nevalida komputilnomo"
+
+#: ../libsoup/soup-tld.c:184 ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "Ne sufiĉe da retregionoj"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "La komputilnomo ne havas ĉefan retregionon"
diff --git a/po/es.po b/po/es.po
index 5b38be6e..8cac889c 100644
--- a/po/es.po
+++ b/po/es.po
@@ -2,15 +2,15 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-# Daniel Mustieles <daniel.mustieles@gmail.com>, 2012.
+# Daniel Mustieles <daniel.mustieles@gmail.com>, 2012, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: libsoup\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-07-16 22:04+0000\n"
-"PO-Revision-Date: 2012-07-17 10:56+0200\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-02-19 12:33+0100\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"Language-Team: Español <gnome-es-list@gnome.org>\n"
"Language: \n"
@@ -18,58 +18,92 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Generator: Gtranslator 2.91.5\n"
-#: ../libsoup/soup-body-input-stream.c:136
-#: ../libsoup/soup-body-input-stream.c:167
-#: ../libsoup/soup-body-input-stream.c:200 ../libsoup/soup-message-io.c:191
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "La conexión terminó inesperadamente"
-#: ../libsoup/soup-converter-wrapper.c:189
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Petición de búsqueda no válida"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "No se puede truncar SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "El flujo de red se cerró inesperadamente"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Falló al cachear completamente el recurso"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "El búfer de salida es demasiado pequeño"
-#: ../libsoup/soup-message-io.c:817 ../libsoup/soup-message-io.c:853
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "No se pudo analizar la respuesta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Codificación de la respuesta HTTP no reconocida"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Se canceló la operación"
-#: ../libsoup/soup-message-io.c:864
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "La operación se bloqueará"
-#: ../libsoup/soup-request.c:142
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "No se pudo analizar la solicitud HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "No se ha proporcionado un URI"
-#: ../libsoup/soup-request.c:152
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "URI «%s» no válida: %s"
-#: ../libsoup/soup-requester.c:219
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "No se pudo analizar el URI «%s»"
-#: ../libsoup/soup-requester.c:253
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Esquema %s de URI no soportado"
-#: ../libsoup/soup-tld.c:143
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "No es un URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "El nombre del equipo es una dirección IP"
-#: ../libsoup/soup-tld.c:164
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "El nombre del equipo no es válido"
-#: ../libsoup/soup-tld.c:179 ../libsoup/soup-tld.c:221
-msgid "Not enough domains"
-msgstr "No hay suficientes dominios"
-
-#: ../libsoup/soup-tld.c:199
+#: ../libsoup/soup-tld.c:235
msgid "Hostname has no base domain"
msgstr "El nombre del equipo no tiene un dominio base"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "No hay suficientes dominios"
diff --git a/po/et.po b/po/et.po
new file mode 100644
index 00000000..b57ce405
--- /dev/null
+++ b/po/et.po
@@ -0,0 +1,85 @@
+# Estonian translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Mattias Põldaru <mahfiaz@gmail.com>, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-03-12 16:51+0000\n"
+"PO-Revision-Date: 2013-03-12 22:43+0300\n"
+"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
+"Language-Team: Estonian <gnome-et-list@gnome.org>\n"
+"Language: et\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Connection terminated unexpectedly"
+msgstr "Ühendus katkes ootamatult"
+
+msgid "Invalid seek request"
+msgstr "Sobimatu kerimise päring"
+
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream-i pole võimalik lühendada"
+
+msgid "Network stream unexpectedly closed"
+msgstr "Võrguvoog sulgus ootamatult"
+
+msgid "Failed to completely cache the resource"
+msgstr "Ressursi täielik puhverdamine nurjus"
+
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Väljundpuhver on liiga väike"
+
+msgid "Could not parse HTTP response"
+msgstr "HTTP vastust polnud võimalik parsida"
+
+msgid "Unrecognized HTTP response encoding"
+msgstr "Tundmatu HTTP vastuse kodeering"
+
+msgid "Operation was cancelled"
+msgstr "Operatsioon katkestati"
+
+msgid "Operation would block"
+msgstr "Operatsioon blokeeruks"
+
+msgid "Could not parse HTTP request"
+msgstr "HTTP päringut polnud võimalik parsida"
+
+#, c-format
+msgid "No URI provided"
+msgstr "URI-d ei antud"
+
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Sobimatu '%s' URI: %s"
+
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI-d '%s' polnud võimalik parsida"
+
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "URI skeem pole toetatud '%s'"
+
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Pole HTTP URI"
+
+msgid "Hostname is an IP address"
+msgstr "Hostinimi on IP-aadress"
+
+msgid "Invalid hostname"
+msgstr "Sobimatu hostinimi"
+
+msgid "Hostname has no base domain"
+msgstr "Hostinimel puudub baasdomeen"
+
+msgid "Not enough domains"
+msgstr "Pole piisavalt domeene"
diff --git a/po/eu.po b/po/eu.po
new file mode 100644
index 00000000..28bbd393
--- /dev/null
+++ b/po/eu.po
@@ -0,0 +1,106 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Iñaki Larrañaga Murgoitio <dooteo@zundan.com>, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-03-26 11:25+0100\n"
+"PO-Revision-Date: 2013-03-24 16:37+0100\n"
+"Last-Translator: Iñaki Larrañaga Murgoitio <dooteo@zundan.com>\n"
+"Language-Team: Basque <itzulpena@euskalgnu.org>\n"
+"Language: eu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.4\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Konexioa ustekabean amaitu da"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Bilaketa-eskaera baliogabea"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Ezin da SoupBodyInputStream trunkatu"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Sarearen korrontea ustekabean itxi da"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Huts egin du baliabidea erabat cachean gordetzean"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Irteeraren bufferra txikiegia da"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Ezin izan da HTTP erantzuna analizatu"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "HTTP erantzunaren kodeketa ezezaguna"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Eragiketa bertan behera utzi da"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Eragiketak blokea dezake"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Ezin izan da HTTP eskaera analizatu"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Ez da URIrik eman"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Baliogabeko '%s' URIa: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Ezin izan da '%s' URIa analizatu"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Onartu gabeko '%s' URI eskema"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Ez da HTTP URIa"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Ostalari-izena IP helbide bat da"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Baliogabeko ostalari-izena"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Ostalari-izenak ez dauka oinarrizko domeinurik"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Ez dago nahikoa domeinurik"
diff --git a/po/fa.po b/po/fa.po
new file mode 100644
index 00000000..74ee1e77
--- /dev/null
+++ b/po/fa.po
@@ -0,0 +1,109 @@
+# Persian translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Arash Mousavi <mousavi.arash@gmail.com>, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-23 18:07+0330\n"
+"Last-Translator: Arash Mousavi <mousavi.arash@gmail.com>\n"
+"Language-Team: Persian\n"
+"Language: fa\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "اتصال به شکل غیرمنتظره‌ای بسته شد"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "درخواست جستجو نامعتبر"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "نمی‌توان SoupBodyInputStream را کوتاه کرد"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "جریان شبکه به‌طور غیرمنتظره‌ای بسته شد"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "به‌طور کامل حافظه‌ی نهان کردن منبع شکست خورد"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "میانگیر خروجی خیلی کوتاه است"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "نمی‌توان پاسخ HTTP را تجزیه کرد"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "کدگذاری پاسخ HTTP شناخته نشد"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "عملیات لغو شده بود"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "عملیات می‌توانست بسته شود"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "نمی‌توان درخواست HTTP را تجزیه کرد"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "هیچ URIای داده نشده است"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "«%s» نامعتبر URI: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "نمی‌توان URI را تجزیه کرد «%s»"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "شِما URI پشتیبانی نشده «%s»"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "آدرس HTTP نیست"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "نام میزبان یک آدرس آی‌پی است"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "نام‌میزبان نامعتبر"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "نام‌میزبان دامنه‌ی پایه ندارد"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "دامنه‌های کافی موجود نیست"
diff --git a/po/fr.po b/po/fr.po
index 44b5a22f..a92665e9 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -1,59 +1,109 @@
# French translation for libsoup.
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
# Pierre Henry <pierrehenry73@yahoo.fr>, 2012.
# Alain Lojewski <allomervan@gmail.com>, 2012.
+# Mickael Albertus <mickael.albertus@gmail.com>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-05-24 20:15+0000\n"
-"PO-Revision-Date: 2012-05-24 19:14+0100\n"
-"Last-Translator: Alain Lojewski <allomervan@gmail.com>\n"
-"Language-Team: French <gnomefr@traduc.org>\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-12 17:51+0100\n"
+"Last-Translator: Claude Paroz <claude@2xlibre.net>\n"
+"Language-Team: GNOME French Team <gnomefr@traduc.org>\n"
+"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "La connexion a été interrompue de manière inattendue"
-#: ../libsoup/soup-converter-wrapper.c:190
-#, c-format
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Requête de recherche invalide"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Impossible de tronquer le SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Le flux réseau s'est arrêté inopinément"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Impossible de mettre la ressource totalement en cache"
+
+#: ../libsoup/soup-converter-wrapper.c:192
msgid "Output buffer is too small"
msgstr "La mémoire tampon de sortie est trop petite"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Impossible d'analyser la réponse HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Codage de réponse HTTP inconnu"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "L'opération a été annulée"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "L'opération aurait bloqué"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Impossible d'analyser la requête HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Aucun URI fourni"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "URI « %s » non valide : %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Impossible d'analyser l'URI « %s »"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Le schéma d'URI « %s » n'est pas pris en charge"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Ce n'est pas un URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Le nom d'hôte est une adresse IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Nom d'hôte non valide"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Le nom d'hôte n'a pas de domaine de base"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Pas assez de domaines"
diff --git a/po/fur.po b/po/fur.po
new file mode 100644
index 00000000..04579ca1
--- /dev/null
+++ b/po/fur.po
@@ -0,0 +1,81 @@
+# Friulian translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# TmTFx <f.t.public@gmail.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup gnome-3-6\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-12-10 10:22+0000\n"
+"PO-Revision-Date: 2012-12-28 13:53+0100\n"
+"Last-Translator: TmTFx <f.t.public@gmail.com>\n"
+"Language-Team: Friulian <fur@li.org>\n"
+"Language: fur\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "No si spietave il termin de conession"
+
+#: ../libsoup/soup-body-input-stream.c:461
+msgid "Invalid seek request"
+msgstr "Domande di ricerche no valide"
+
+#: ../libsoup/soup-body-input-stream.c:489
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "No puès cjonçâ SoupBodyInputStream"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Il buffer di jessude al è masse piçul"
+
+#: ../libsoup/soup-message-io.c:818 ../libsoup/soup-message-io.c:854
+msgid "Operation was cancelled"
+msgstr "L'operazion a je stade scancelade"
+
+#: ../libsoup/soup-message-io.c:865
+msgid "Operation would block"
+msgstr "L'operazion a sarà blocade"
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "No'l è stât furnît un URI"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "URI '%s' no valit: %s"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "No puès analizâ l'URI '%s'"
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Scheme URI '%s' no supuartât"
+
+#: ../libsoup/soup-tld.c:154
+msgid "Hostname is an IP address"
+msgstr "Il non host al è un indiriz IP"
+
+#: ../libsoup/soup-tld.c:175
+msgid "Invalid hostname"
+msgstr "Il non host a no'l è valit"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "Il non host a no'l a un domini di base"
+
+#: ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "No vonde dominis"
diff --git a/po/gl.po b/po/gl.po
index c493a55c..b2cfc9bb 100644
--- a/po/gl.po
+++ b/po/gl.po
@@ -1,71 +1,106 @@
# Galician translation for libsoup.
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-# Fran Dieguez <frandieguez@gnome.org>, 2012.
-#
+# Fran Dieguez <frandieguez@gnome.org>, 2012, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-06-24 23:26+0200\n"
-"PO-Revision-Date: 2012-06-24 23:26+0200\n"
+"POT-Creation-Date: 2013-02-20 10:39+0100\n"
+"PO-Revision-Date: 2013-02-20 10:40+0200\n"
"Last-Translator: Fran Dieguez <frandieguez@gnome.org>\n"
-"Language-Team: Galician <gnome-l10n-gl@gnome.org>\n"
+"Language-Team: gnome-l10n-gl@gnome.org\n"
"Language: gl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n!=1);\n"
-"X-Generator: Gtranslator 2.91.5\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "A conexión rematou de forma non esperada"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Petición de busca non válida"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Non é posíbel truncar SoupbodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Fluxo de rede pechado de forma non esperada"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Produciuse un fallo ao cachear completamente o recurso"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "O búfer de saída é demasiado pequeno"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Non é posíbel analizar a resposta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Codificación da resposta HTTP non recoñecida"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "A operación foi cancelada"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "A operación bloquearase"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Non é posíbel analizar a consulta HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Non se forneceu un URI"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "URI «%s» non válida: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4211
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Non é posíbel analizar o URI «%s»"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4248
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Esquema de URI «%s» non admitido"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4270
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Non é unha URI de HTTP"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "O nome do computador é un enderezo IP"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "O nome do computador non é válido"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "O nome do computador non ten un dominio base"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Non hai dominios dabondo"
diff --git a/po/gu.po b/po/gu.po
new file mode 100644
index 00000000..19d46368
--- /dev/null
+++ b/po/gu.po
@@ -0,0 +1,111 @@
+# Gujarati translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# , 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug."
+"cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-12 16:21+0530\n"
+"Last-Translator: \n"
+"Language-Team: American English <kde-i18n-doc@kde.org>\n"
+"Language: gu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 1.0\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "જોડાણ અનિચ્છનીય રીતે તૂટી ગયુ"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "અયોગ્ય સીક માંગણી"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream કાઢી શકાતુ નથી"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "નેટવર્ક સ્ટ્રીમ અનિચ્છનીય રીતે બંધ થઇ ગઇ"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "સ્ત્રોતને સંપૂર્ણપણે કેશ કરવામાં નિષ્ફળતા"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "આઉટપુટ બફર ઘણુ નાનું છે"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "HTTP જવાબનું પદચ્છેદન કરી શક્યા નહિં"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "બિનઓળખાયેલ HTTP જવાબ એનકોડીંગ"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "ક્રિયા રદ થયેલ હતી"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "ક્રિયા રોકી રખાશે"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "HTTP માંગણીનુ પદચ્છેદન કરી શક્યા નહિં"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "URI પૂરુ પાડેલ નથી"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "અમાન્ય '%s' URI: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s' ને પદચ્છેદન કરી શક્યા નહિં"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "બિનઆધારભૂત URI યોજના '%s'"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI નથી"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "યજમાનનામ એ IP સરનામું છે"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "અમાન્ય યજમાનનામ"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "યજમાન પાસે મૂળ ડોમેઇન નથી"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "પૂરતુ ડોમેઇન નથી"
+
diff --git a/po/he.po b/po/he.po
index f201ed5c..7658eed8 100644
--- a/po/he.po
+++ b/po/he.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: libsoup gnome\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-12 18:58+0300\n"
-"PO-Revision-Date: 2012-07-12 18:58+0200\n"
+"POT-Creation-Date: 2013-02-21 19:54+0200\n"
+"PO-Revision-Date: 2013-02-21 19:55+0200\n"
"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
"Language-Team: Hebrew <sh.yaron@gmail.com>\n"
"Language: \n"
@@ -20,57 +20,93 @@ msgstr ""
"X-Poedit-Country: ISRAEL\n"
"X-Poedit-SourceCharset: utf-8\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203
-#: ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205
+#: ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "החיבור הופסק באופן בלתי צפוי"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "בקשת חיפוש שגויה"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "לא ניתן לקצץ את SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "תזרים הרשת נסגר בפתאומיות"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "שמירת המשאב במלואו במטמון נכשלה"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "אוגר הפלט קטן מדי"
-#: ../libsoup/soup-message-io.c:836
-#: ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "לא ניתן לנתח את תגובת ה־HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "קידוד תגובת ה־HTTP אינו מוכר"
+
+#: ../libsoup/soup-message-io.c:846
+#: ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "הפעולה בוטלה"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "הפעולה תיחסם"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "לא ניתן לנתח את תגובת ה־HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "לא סופקה כתובת"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "כתובת '%s' שגויה: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4211
#, c-format
msgid "Could not parse URI '%s'"
msgstr "לא ניתן לנתח את הכתובת '%s'"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4248
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "תבנית הכתובת אינה נתמכת '%s'"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4270
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "כתובת שאינה HTTP"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "שם המארח הוא כתובת IP"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "שם מארח שגוי"
-#: ../libsoup/soup-tld.c:181
-#: ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "לשם המארח אין שם מתחם בסיסי"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "אין מספיק שמות מתחם"
diff --git a/po/hi.po b/po/hi.po
new file mode 100644
index 00000000..de85b345
--- /dev/null
+++ b/po/hi.po
@@ -0,0 +1,113 @@
+# Hindi translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# 1 <1>, 2012.
+# 1 Pratibha kumari <1pratibharoshan1526@gmail.com>, 2012.
+# raj <raj>, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-20 16:47+0530\n"
+"Last-Translator: raj <raj>\n"
+"Language-Team: Hindi <kde-i18n-doc@kde.org>\n"
+"Language: hi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.5\n"
+"X-Project-Style: gnome\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "मिलाप अनपेक्षित रुप से खतम हो गया "
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "अमान्य खोज अनुरोध"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream का नहीं कर सकता"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "संजाल स्ट्रीम अप्रत्याशित रूप से बंद हो गया"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "संसाधन को पूरी तरह कैश करने में विफल"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "निर्गम बफर बहुत छोटा है"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "HTTP अनुक्रिया विश्लेषित नहीं कर सका"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "अपरिचित HTTP अनुक्रिया एन्कोडिंग"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "आपरेशन रद्द कर दिया गया"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "आपरेशन बंद हो जाएगा"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "HTTP आग्रह विश्लेषित नहीं कर सका"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "यूआरआई नहीं प्रदान किया गया"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "यूआरआई '%s' अमान्य: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s' पद व्याख्या नहीं हो सका"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "URI योजना '%s' को सहारा नहीं"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "कोई HTTP URI नहीं"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "मेजबाननाम एक आईपी पता है"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "मेजबाननाम मान्य नहीं"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "मेजबाननाम को आधार डोमेन नहीं है"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "डोमेन पर्याप्त नहीं"
+
diff --git a/po/hu.po b/po/hu.po
new file mode 100644
index 00000000..dc3311ea
--- /dev/null
+++ b/po/hu.po
@@ -0,0 +1,112 @@
+# Hungarian translation of libsoup
+# Copyright (C) 2012. Free Software Foundation, Inc.
+# This file is distributed under the same license as the libsoup package.
+#
+# Gabor Kelemen <kelemeng at gnome dot hu>, 2012.
+# Balázs Úr <urbalazs at gmail dot com>, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-15 12:35+0100\n"
+"Last-Translator: Balázs Úr <urbalazs at gmail dot com>\n"
+"Language-Team: Hungarian <gnome-hu-list at gnome dot org>\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.2\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Kapcsolat váratlanul megszakítva"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Érvénytelen pozicionálási kérés"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "A SoupBodyInputStream nem csonkítható"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Az hálózati adatfolyam váratlanul lezárult"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Nem sikerült teljesen gyorsítótárazni az erőforrást"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "A kimeneti puffer túl kicsi"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "Nem dolgozható fel a HTTP válasz"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Ismeretlen HTTP válasz kódolás"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "A művelet megszakítva"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "A művelet blokkoló lenne"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "Nem dolgozható fel a HTTP kérés"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Nincs megadva URI"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Érvénytelen „%s” URI: „%s”"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Nem dolgozható fel a(z) „%s” URI"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Nem támogatott URI séma: „%s”"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Ez nem HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "A gépnév egy IP-cím"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Érvénytelen gépnév"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "A gépnévnek nincs alap tartománya"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Nincs elég tartomány"
+
diff --git a/po/id.po b/po/id.po
index 336740cf..96307099 100644
--- a/po/id.po
+++ b/po/id.po
@@ -1,68 +1,109 @@
# Indonesian translation for libsoup.
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
-# Andika Triwidada <andika@gmail.com>, 2012.
#
+# Andika Triwidada <andika@gmail.com>, 2012.
+# Dirgita <dirgitadevina@yahoo.co.id>, 2012, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-06-14 18:14+0000\n"
-"PO-Revision-Date: 2012-06-28 10:25+0700\n"
-"Last-Translator: Andika Triwidada <andika@gmail.com>\n"
+"POT-Creation-Date: 2013-09-16 22:19+0000\n"
+"PO-Revision-Date: 2013-09-18 20:55+0700\n"
+"Last-Translator: Dirgita <dirgitadevina@yahoo.co.id>\n"
"Language-Team: Indonesian <gnome@i15n.org>\n"
+"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 1.5\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:140
+#: ../libsoup/soup-body-input-stream.c:171
+#: ../libsoup/soup-body-input-stream.c:204 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Sambungan terputus secara tak diharapkan"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:462
+msgid "Invalid seek request"
+msgstr "Permintaan seek yang tak valid"
+
+#: ../libsoup/soup-body-input-stream.c:490
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Tak bisa memenggal SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:73
+msgid "Network stream unexpectedly closed"
+msgstr "Lalu lintas jaringan putus tak terduga"
+
+#: ../libsoup/soup-cache-input-stream.c:290
+msgid "Failed to completely cache the resource"
+msgstr "Gagal melengkapi tembolok sumber"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Penyangga keluaran terlalu kecil"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Tak dapat mengurai tanggapan HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Enkode tanggapan HTTP tak dikenal"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Operasi dibatalkan"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Operasi akan memblokir"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Tak dapat mengurai permintaan HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "URI tak diberikan"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "URI '%s' tak valid: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4259
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Tak bisa mengurai URI '%s'"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4296
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Skema URI '%s' tak didukung"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4318
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Bukan URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Nama host adalah suatu alamat IP"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Nama host tak valid"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Nama host tidak memiliki domain dasar"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Tak cukup domain"
diff --git a/po/it.po b/po/it.po
new file mode 100644
index 00000000..ba800dba
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,106 @@
+# Italian translations for libsoup package
+# Copyright (C) 2012, 2013 the Free Software Foundation, Inc.
+# This file is distributed under the same license as the libsoup package.
+# Milo Casagrande <milo@casagrande.name>, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-03-11 16:59+0100\n"
+"PO-Revision-Date: 2013-03-11 17:00+0100\n"
+"Last-Translator: Milo Casagrande <milo@casagrande.name>\n"
+"Language-Team: Italian <tp@lists.linux.it>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Connessione terminata inaspettatamente"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Richiesta di posizionamento non valida"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Impossibile troncare SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Flusso di rete chiuso inaspettatamente"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Salvataggio in memoria della risorsa non riuscito"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Il buffer di uscita è troppo piccolo"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Impossibile analizzare la risposta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Codifica risposta HTTP non riconosciuta"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "L'operazione è stata annullata"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "L'operazione potrebbe bloccarsi"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Impossibile analizzare la richiesta HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Nessun URI fornito"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "URI «%s» non valido: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Impossibile analizzare l'URI «%s»"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Schema URI «%s» non supportato"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Non è uno URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Il nome host è un indirizzo IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Nome host non valido"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Il nome host non ha un dominio di base"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Domini insufficienti"
diff --git a/po/ja.po b/po/ja.po
new file mode 100644
index 00000000..795fa84a
--- /dev/null
+++ b/po/ja.po
@@ -0,0 +1,81 @@
+# libsoup ja.po
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Takayuki KUSANO <AE5T-KSN@asahi-net.or.jp>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-11-02 19:31+0000\n"
+"PO-Revision-Date: 2012-10-12 13:30+0900\n"
+"Last-Translator: Takayuki KUSANO <AE5T-KSN@asahi-net.or.jp>\n"
+"Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "接続が突然切断されました"
+
+#: ../libsoup/soup-body-input-stream.c:461
+msgid "Invalid seek request"
+msgstr "無効な seek 要求"
+
+#: ../libsoup/soup-body-input-stream.c:489
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream は truncate できません"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "出力バッファーが小さすぎます"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "動作が中止されました"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "動作はブロックします"
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "URI が与えられていません"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "不正なスキーム '%s' の URI: %s"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s' をパースできませんでした"
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "サポートされていない URI のスキーム '%s'"
+
+#: ../libsoup/soup-tld.c:154
+msgid "Hostname is an IP address"
+msgstr "ホスト名が IP アドレスです"
+
+#: ../libsoup/soup-tld.c:175
+msgid "Invalid hostname"
+msgstr "不正なホスト名です"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "ホスト名にトップレベルドメインがありません"
+
+# ソースコードと http://www.nic.ad.jp/ja/dom/system.html 参照
+#: ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "ドメイン名が十分な数のラベルがありません"
diff --git a/po/kn.po b/po/kn.po
new file mode 100644
index 00000000..98bd11e4
--- /dev/null
+++ b/po/kn.po
@@ -0,0 +1,109 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Shankar Prasad <svenkate@redhat.com>, 2012, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product="
+"libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-24 08:30-0400\n"
+"Last-Translator: Shankar Prasad <svenkate@redhat.com>\n"
+"Language-Team: Kannada <kde-i18n-doc@kde.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: kn\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Zanata 3.2.3\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205
+#: ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "ಸಂಪರ್ಕವು ಅನಿರೀಕ್ಷಿತವಾಗಿ ಕೊನೆಗೊಂಡಿದೆ"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "ಅಮಾನ್ಯವಾದ ಕೋರಿಕೆಯ ಮನವಿ"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream ಅನ್ನು ತುಂಡರಿಸಲಾಗಿಲ್ಲ"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "ಜಾಲಬಂಧ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಅನಿರೀಕ್ಷಿತವಾಗಿ ಮುಚ್ಚಲಾಗಿದೆ"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "ಸಂಪನ್ಮೂಲವನ್ನು ಸಂಪೂರ್ಣವಾಗಿ ಕ್ಯಾಶ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲಗೊಂಡಿದೆ"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "ಔಟ್‌ಪುಟ್ ಬಫರ್ ಬಹಳ ಚಿಕ್ಕದಾಗಿದೆ"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "HTTP ಪ್ರತ್ಯುತ್ತರವನ್ನು ಪಾರ್ಸ್ ಮಾಡಲಾಗಿಲ್ಲ"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "ಗುರುತಿಸಲಾಗದ HTTP ಪ್ರತಿಕ್ರಿಯೆ ಎನ್ಕೋಡಿಂಗ್"
+
+#: ../libsoup/soup-message-io.c:846
+#: ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "ಕಾರ್ಯಾಚರಣೆ ತಡೆಯಲ್ಪಡಬಹುದು"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "HTTP ಮನವಿಯನ್ನು ಪಾರ್ಸ್ ಮಾಡಲಾಗಿಲ್ಲ"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "ಯಾವುದೆ URI ಅನ್ನು ಒದಗಿಸಲಾಗಿಲ್ಲ."
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "ಅಮಾನ್ಯವಾದ '%s' URI:'%s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "'%s' URL ಅನ್ನು ಪಾರ್ಸ್ ಮಾಡಲಾಗಿಲ್ಲ"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "ಬೆಂಬಲವಿಲ್ಲದ ಯುಆರ್ಐ ಮಾದರಿ '%s'"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "ಒಂದು HTTP URI ಅಲ್ಲ"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "ಆತಿಥೇಯವು ಒಂದು IP ವಿಳಾಸವಾಗಿದೆ"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "ಅಮಾನ್ಯವಾದ ಅತಿಥೇಯದ ಹೆಸರು"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "ಆತಿಥೇಯದ ಹೆಸರಿನಲ್ಲಿ ಯಾವುದೆ ಮೂಲ ಡೊಮೈನ್ ಇಲ್ಲ"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "ಬೇಕಾದಷ್ಟು ಡೊಮೈನ್‌ಗಳಿಲ್ಲ"
diff --git a/po/ko.po b/po/ko.po
new file mode 100644
index 00000000..c07240e5
--- /dev/null
+++ b/po/ko.po
@@ -0,0 +1,109 @@
+# Korean translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# eukim <eukim@redhat.com>, 2012.
+# Changwoo Ryu <cwryu@debian.org>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-22 04:46+0900\n"
+"Last-Translator: Changwoo Ryu <cwryu@debian.org>\n"
+"Language-Team: Korean <gnome-kr@googlegroups.com>\n"
+"Language: ko\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Lokalize 1.0\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "예상치 않게 연결이 중지됨"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "탐색 요청이 잘못되었습니다"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream을 자를 수 없습니다"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "네트워크 스트림이 예상치 못하게 닫혔습니다"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "리소스를 완전히 캐시에 저장하는데 실패했습니다"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "출력 버퍼가 너무 작습니다"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "HTTP 응답을 구문 분석할 수 없습니다"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "HTTP 응답 인코딩을 알 수 없습니다"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "작업이 취소되었습니다"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "작업이 블럭되었습니다"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "HTTP 요청을 구문 분석할 수 없습니다"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "URI가 없습니다"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "잘못된 '%s' URI: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "'%s' URI를 구문 분석할 수 없습니다"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "'%s' URI 스킴을 지원하지 않습니다"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI가 아닙니다"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "호스트 이름은 IP 주소입니다"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "잘못된 호스트 이름입니다"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "호스트 이름에 기본 도메인이 없습니다"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "도메인이 충분하지 않습니다"
diff --git a/po/lt.po b/po/lt.po
index 30c6975b..ae9183e4 100644
--- a/po/lt.po
+++ b/po/lt.po
@@ -1,73 +1,111 @@
# Lithuanian translation for libsoup.
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
-# Aurimas Černius <aurisc4@gmail.com>, 2012.
+# Aurimas Černius <aurisc4@gmail.com>, 2012, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
-"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-07-14 11:19+0000\n"
-"PO-Revision-Date: 2012-07-14 18:48+0300\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-02-24 21:55+0200\n"
"Last-Translator: Aurimas Černius <aurisc4@gmail.com>\n"
-"Language-Team: Lithuanian <gnome-lt@lists.akl.lt>\n"
+"Language-Team: Lietuvių <gnome-lt@lists.akl.lt>\n"
"Language: lt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n"
+"%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Gtranslator 2.91.6\n"
-#: ../libsoup/soup-body-input-stream.c:136
-#: ../libsoup/soup-body-input-stream.c:167
-#: ../libsoup/soup-body-input-stream.c:200
-#: ../libsoup/soup-message-io.c:193
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Ryšys netikėtai nutrūko"
-#: ../libsoup/soup-converter-wrapper.c:189
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Netinkama paieškos užklausa"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Negalima trumpinti SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Tinklo srautas netikėtai užsivėrė"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Nepavyko visiškai patalpinti resurso į podėlį"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Išvesties buferis yra per mažas"
-#: ../libsoup/soup-message-io.c:817
-#: ../libsoup/soup-message-io.c:853
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "Nepavyko perskaityti URI „%s“Nepavyko perskaityti HTTP atsako"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Neatpažinta HTTP atsako koduotė"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Veiksmas buvo nutrauktas"
-#: ../libsoup/soup-message-io.c:864
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Veiksmas blokuosis"
-#: ../libsoup/soup-request.c:142
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "Nepavyko perskaityti HTTP užklausos"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Nepateiktas URI"
-#: ../libsoup/soup-request.c:152
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Netinkamas „%s“ URI: %s"
-#: ../libsoup/soup-requester.c:219
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Nepavyko perskaityti URI „%s“"
-#: ../libsoup/soup-requester.c:253
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Nepalaikoma URI schema „%s“"
-#: ../libsoup/soup-tld.c:146
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Ne HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Serverio vardas arba IP adresas"
-#: ../libsoup/soup-tld.c:167
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Netinkamas serverio vardas"
-#: ../libsoup/soup-tld.c:182
-#: ../libsoup/soup-tld.c:220
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Serverio vardas neturi bazinio domeno"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Nepakanka domenų"
-
diff --git a/po/lv.po b/po/lv.po
new file mode 100644
index 00000000..c22e57a4
--- /dev/null
+++ b/po/lv.po
@@ -0,0 +1,113 @@
+# Latvian translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# Tranzistors <rudolfs.mazurs@gmail.com>, 2012.
+# Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>, 2012, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-17 20:11+0200\n"
+"Last-Translator: Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>\n"
+"Language-Team: Latvian <lata-l10n@googlegroups.com>\n"
+"Language: lv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : "
+"2);\n"
+"X-Generator: Lokalize 1.4\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Savienojums tika negaidīti pārtraukts"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Nederīgs meklēšanas pieprasījums"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Nevar apraut SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Tīkla straume negaidīti aizvērās"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Neizdevās resursu pilnībā noglabāt kešatmiņā"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Izvades buferis ir pārāk mazs"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "Nevarēja parsēt HTTP atbildi"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Neatpazīts HTTP atbildes kodējums"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Darbība tika atcelta"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Darbība bloķētu"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "Nevarēja parsēt HTTP pieprasījumu"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Nav sniegts URI"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Nederīgs “%s” URI — %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Nevarēja parsēt URI “%s”"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Neatbalstīta URI shēma “%s”"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Nav HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Datora nosaukums ir IP adrese"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Nederīgs datora nosaukums"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Datora nosaukumam nav bāzes domēna"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Nepietiekami daudz domēnu"
+
diff --git a/po/ml.po b/po/ml.po
new file mode 100644
index 00000000..4b515208
--- /dev/null
+++ b/po/ml.po
@@ -0,0 +1,109 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Ani Peter <apeter@redhat.com>, 2012, 2013.
+# Anish A <aneesh.nl@gmail.com>, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-03-24 12:31+0000\n"
+"PO-Revision-Date: 2013-03-25 11:40+0530\n"
+"Last-Translator: Ani Peter <peter.ani@gmail.com>\n"
+"Language-Team: American English <kde-i18n-doc@kde.org>\n"
+"Language: ml\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 1.5\n"
+"X-Project-Style: gnome\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "കണക്ഷന്‍ അപ്രതീക്ഷതമായി അവസാനിയ്ക്കുന്നു"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "തെറ്റായ തെരച്ചില്‍ ആവശ്യം"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream ട്രക്കേറ്റ് ചെയ്യുവാന്‍ സാധ്യമല്ല"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "ശൃംഖല സ്ട്രീം വിചാരിക്കാതെ അടച്ചു"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "ശ്രോതസ്സ് കാഷ് ചെയ്യുന്നതു് പൂര്‍ണ്ണമായി പരാജയപ്പെട്ടു"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "ഔട്ട്പുട്ട് ബഫര്‍ വളരെ ചെറുതാണു്"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "HTTP മറുപടി പാഴ്സ് ചെയ്യുവാന്‍ സാധ്യമായില്ല"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "HTTP മറുപടിയുടെ രൂപം തിരിച്ചറിയാനായില്ല"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "പ്രക്രിയ റദ്ദാക്കിയിരിയ്ക്കുന്നു"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "പ്രക്രിയ തടസ്സപ്പെടുത്തുന്നു"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "HTTP മറുപടി പാഴ്സ് ചെയ്യുവാനായില്ല"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "യുആര്‍ഐ ലഭ്യമാക്കിയിട്ടില്ല"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "തെറ്റായ '%s' യുആര്‍ഐ: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "യുആര്‍ഐ '%s' പാഴ്സ് ചെയ്യുവാനായില്ല"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "പിന്തുണയില്ലാത്ത യുആര്‍ഐ സ്കീം '%s'"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI അല്ല"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "ഹോസ്റ്റ്നാമം ഒരു ഐപി വിലാസമാകുന്നു"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "തെറ്റായ ഹോസ്റ്റ്നാമം"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "ഹോസ്റ്റ്നാമത്തിനു് ബെയിസ് ഡൊമെയില്‍ ലഭ്യമല്ല"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "ആവശ്യമായ ഡൊമെയിനുകള്‍ ലഭ്യമല്ല"
+
diff --git a/po/mr.po b/po/mr.po
new file mode 100644
index 00000000..e2ca1630
--- /dev/null
+++ b/po/mr.po
@@ -0,0 +1,111 @@
+# Marathi translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# Sandeep Shedmake <sshedmak@redhat.com>, 2012, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-20 06:56+0530\n"
+"Last-Translator: Sandeep Shedmake <sshedmak@redhat.com>\n"
+"Language-Team: Marathi <maajhe-sanganak@freelists.org>\n"
+"Language: mr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "जोडणी अनपेक्षितपणे बंद झाली"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "अवैध सीक विनंती"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream ट्रंकेट करणे अशक्य"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "नेटवर्क स्ट्रिम अनपेक्षितरित्या बंद झाले"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "रिसोअर्स संपूर्णपणे कॅशे करण्यास अपयशी"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "आउटपुट बफर खूपच लहान आहे"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "HTTP प्रतिसाद वाचणे अशक्य"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "अपरिचीत HTTP प्रतिसाद एंकोडिंग"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "कार्य रद्द केले"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "कार्य बंधिस्त करू शकते"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "HTTP विनंती वाचणे अशक्य"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "URI पुरवले नाही"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "अवैध '%s' URI: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s' वाचणे अशक्य"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "असमर्थीत URI सुत्रयोजना '%s'"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI नाही"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "यजमाननाव IP पत्ता आहे"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "अवैध यजमाननाव"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "यजमाननावात बेस डोमैन नाही"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "अतिरिक्त डोमैन्स् आढळले नाही"
+
diff --git a/po/nb.po b/po/nb.po
index 85295b12..b2293d90 100644
--- a/po/nb.po
+++ b/po/nb.po
@@ -1,14 +1,14 @@
# Norwegian bokmål translation of libsoup.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-# Kjartan Maraas <kmaraas@gnome.org>, 2012.
+# Kjartan Maraas <kmaraas@gnome.org>, 2012-2013.
#
msgid ""
msgstr ""
-"Project-Id-Version: libsoup 3.5.x\n"
+"Project-Id-Version: libsoup 3.7.x\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-10 07:56+0200\n"
-"PO-Revision-Date: 2012-07-10 07:57+0200\n"
+"POT-Creation-Date: 2013-02-25 17:06+0100\n"
+"PO-Revision-Date: 2013-02-25 17:07+0100\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
"Language: \n"
@@ -16,53 +16,90 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Tilkoblingen ble brutt uventet"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Ugyldig søkeforespørsel"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Kan ikke avkorte SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Nettverksstrømmen ble lukket på uventet vis"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Klarte ikke å mellomlagre ressursen fullt ut"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Buffer for utdata er for liten"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Kunne ikke tolke HTTP-svar"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Ikke gjenkjent koding av HTTP-svar"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Operasjonen ble avbrutt"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Operasjonen ville blokkere"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Kunne ikke tolke HTTP-forespørsel"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Ingen URI ble oppgitt"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Ugyldig «%s» URI: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4211
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Kunne ikke tolke URI «%s»"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4248
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "URI-skjema «%s» er ikke støttet"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4270
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Ikke en HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Vertsnavnet er en IP-adresse"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Ugyldig vertsnavn"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Vertsnavnet har ikke noe grunndomene"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Ikke mange nok domener"
diff --git a/po/nl.po b/po/nl.po
new file mode 100644
index 00000000..77fc8526
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,109 @@
+# Dutch translation for libsoup.
+# Copyright (C) 2013 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Reinout van Schouwen <reinout@gmail.com>, 2013.
+# Reinout van Schouwen <reinouts@gnome.org>, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsou"
+"p&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-09-16 22:19+0000\n"
+"PO-Revision-Date: 2013-09-18 23:22+0200\n"
+"Last-Translator: Reinout van Schouwen <reinouts@gnome.org>\n"
+"Language-Team: Dutch <vertaling@vrijschrift.org>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Virtaal 0.7.1\n"
+"X-Project-Style: gnome\n"
+
+#: ../libsoup/soup-body-input-stream.c:140
+#: ../libsoup/soup-body-input-stream.c:171
+#: ../libsoup/soup-body-input-stream.c:204 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Verbinding werd onverwacht verbroken"
+
+#: ../libsoup/soup-body-input-stream.c:462
+msgid "Invalid seek request"
+msgstr "Ongeldige zoekaanvraag"
+
+#: ../libsoup/soup-body-input-stream.c:490
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Kan SoupBodyInputStream niet afkappen"
+
+#: ../libsoup/soup-cache-input-stream.c:73
+msgid "Network stream unexpectedly closed"
+msgstr "Netwerkstroom onverwacht gesloten"
+
+#: ../libsoup/soup-cache-input-stream.c:290
+msgid "Failed to completely cache the resource"
+msgstr "Volledig bufferen van de bron is mislukt"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Uitvoerbuffer is te klein"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Kan HTTP-antwoord niet verwerken"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Niet-herkende HTTP-antwoordcodering"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Bewerking is geannuleerd"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Bewerking zou blokkeren"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Kan HTTP-aanvraag niet verwerken"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Geen URI aangeleverd"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Ongeldige ‘%s’-URI: %s"
+
+#: ../libsoup/soup-session.c:4259
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Kon URI ‘%s’ niet verwerken"
+
+#: ../libsoup/soup-session.c:4296
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Niet-ondersteund URI-schema ‘%s’."
+
+#: ../libsoup/soup-session.c:4318
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Geen HTTP-URI"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Hostnaam is een IP-adres"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Ongeldige hostnaam"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Hostnaam heeft geen basisdomein"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Niet genoeg domeinen"
diff --git a/po/or.po b/po/or.po
new file mode 100644
index 00000000..f6be296d
--- /dev/null
+++ b/po/or.po
@@ -0,0 +1,110 @@
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Manoj Kumar Giri <mgiri@redhat.com>, 2012, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-22 18:02+0530\n"
+"Last-Translator: Manoj Kumar Giri <mgiri@redhat.com>\n"
+"Language-Team: Oriya <oriya-it@googlegroups.com>\n"
+"Language: or\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.5\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "ସଂଯୋଗଟି ଅପ୍ରତ୍ୟାଶିତ ଭାବରେ ବନ୍ଦ ହୋଇଛି"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "ଅବୈଧ ଅନୁସନ୍ଧାନ ଅନୁରୋଧ"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr " SoupBodyInputStream କୁ ବିଚ୍ଛିନ୍ନ କରିହେବ ନାହିଁ "
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "ନେଟୱର୍କ୍‌ ଧାରା ଅପ୍ରତ୍ୟାଶିତ ଭାବରେ ବନ୍ଦ ହୋଇଛି"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "ଉତ୍ସକୁ ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ କ୍ୟାଶେ କରିବାରେ ବିଫଳ"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "ଫଳାଫଳ ବଫରଟି ଅତି ଛୋଟ ଅଟେ"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "HTTP ଉତ୍ତର ବିଶ୍ଳେଷଣ କରିପାରିଲା ନାହିଁ"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "ଅଚିହ୍ନା HTTP ଉତ୍ତର ସାଙ୍କେତିକରଣ"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "ପ୍ରୟୋଗକୁ ବାତିଲ କରାଯାଇଛି"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "ପ୍ରୟୋଗଟି ବନ୍ଦ ହୋଇପାରେ"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "HTTP ଅନୁରୋଧକୁ ବିଶ୍ଳେଷଣ କରିପାରିଲା ନାହିଁ"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "କୌଣସି URI ଦିଆଯାଇ ନାହିଁ"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "ଅବୈଧ '%s' URI: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s'କୁ ବିଶ୍ଳେଷଣ କରିପାରିଲା ନାହିଁ"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "ଅସମର୍ଥିତ URI ଯୋଜନା '%s'"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "ଏହା ଏକ HTTP URI ନୁହଁ"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "ହୋଷ୍ଟନାମଟି ଏକ IP ଠିକଣା ଅଟେ"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "ଅବୈଧ ହୋଷ୍ଟ ନାମ"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "ହୋଷ୍ଟ ନାମରେ କୌଣସି ମୂଳ ଡମେନ ନଥାଏ"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "ଯଥେଷ୍ଟ ଡମେନ ନାହିଁ"
+
diff --git a/po/pa.po b/po/pa.po
index 8ea8198f..3edbf498 100644
--- a/po/pa.po
+++ b/po/pa.po
@@ -2,59 +2,110 @@
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
#
-# A S Alam <aalam@users.sf.net>, 2012.
+# A S Alam <aalam@users.sf.net>, 2012, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-05-16 03:05+0000\n"
-"PO-Revision-Date: 2012-05-18 07:27+0530\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-02-26 07:36+0530\n"
"Last-Translator: A S Alam <aalam@users.sf.net>\n"
"Language-Team: Punjabi/Panjabi <punjabi-users@lists.sf.net>\n"
+"Language: pa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: pa\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Lokalize 1.4\n"
+"X-Generator: Lokalize 1.5\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "ਕੁਨੈਕਸ਼ਨ ਅਚਾਨਕ ਬੰਦ ਹੋ ਗਿਆ"
-#: ../libsoup/soup-converter-wrapper.c:185
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "ਸੀਕ ਮੰਗ ਗਲਤ ਹੈ"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream ਨੂੰ ਛੋਟਾ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "ਨੈੱਟਵਰਕ ਸਟਰੀਮ ਅਚਾਨਕ ਬੰਦ ਹੋਈ"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "ਸਰੋਤ ਨੂੰ ਪੂਰੀ ਕੈਸ਼ ਕਰਨ ਲਈ ਫੇਲ੍ਹ ਹੈ"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "ਆਉਟਪੁੱਟ ਬਫ਼ਰ ਬਹੁਤ ਛੋਟਾ ਹੈ"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "HTTP ਨੂੰ ਪਾਰਸ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "ਬੇਪਛਾਣ HTTP ਜਵਾਬ ਇੰਕੋਡਿੰਗ"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "ਕਾਰਵਾਈ ਰੱਦ ਕੀਤੀ ਗਈ"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "ਕਾਰਵਾਈ ਉੱਤੇ ਪਾਬੰਦੀ ਹੋਵੇਗੀ"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "HTTP ਮੰਗ ਨੂੰ ਪਾਰਸ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "ਕੋਈ URI ਨਹੀਂ ਦਿੱਤਾ"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "ਗਲਤ '%s' URI: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "URI '%s' ਨੂੰ ਪਾਰਸ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "ਗ਼ੈਰ-ਸਹਾਇਕ URI ਸਕੀਮ '%s'"
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI ਨਹੀਂ ਹੈ"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "ਹੋਸਟ ਨਾਂ IP ਐਡਰੈਸ ਹੈ"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "ਗਲਤ ਹੋਸਟ-ਨਾਂ"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "ਹੋਸਟ-ਨਾਂ ਲਈ ਬੇਸ ਡੋਮੇਮ ਨਹੀਂ ਹੈ"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "ਲੋੜੀਦੀਆਂ ਡੋਮੇਨ ਨਹੀਂ ਹੈ"
+
diff --git a/po/pl.po b/po/pl.po
index a4ccffcb..be2b5bfe 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -4,12 +4,14 @@
# pomóc w jego rozwijaniu i pielęgnowaniu, napisz do nas:
# gnomepl@aviary.pl
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+# Piotr Drąg <piotrdrag@gmail.com>, 2012-2013.
+# Aviary.pl <gnomepl@aviary.pl>, 2012-2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-06-03 15:35+0200\n"
-"PO-Revision-Date: 2012-06-03 15:36+0200\n"
+"POT-Creation-Date: 2013-02-18 16:13+0100\n"
+"PO-Revision-Date: 2013-02-18 16:14+0100\n"
"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
"Language-Team: Polish <gnomepl@aviary.pl>\n"
"Language: pl\n"
@@ -21,41 +23,90 @@ msgstr ""
"X-Poedit-Language: Polish\n"
"X-Poedit-Country: Poland\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Połączenie zostało nieoczekiwanie zakończone"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Nieprawidłowe żądanie przewinięcia"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Nie można skrócić SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Strumień sieciowy został nieoczekiwanie zamknięty"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Całkowite umieszczenie zasobu w pamięci podręcznej się nie powiodło"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Bufor wyjściowy jest za mały"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Nie można przetworzyć odpowiedzi HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Nierozpoznane kodowanie odpowiedzi HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Anulowano działanie"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Działanie spowodowałoby zablokowanie"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Nie można przetworzyć żądania HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Nie podano adresu URI"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Nieprawidłowy adres URI \"%s\": %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Nie można przetworzyć adresu URI \"%s\""
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Nieobsługiwany schemat adresu URI \"%s\""
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Nie jest adresem URI protokołu HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Nazwa komputera jest adresem IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Nieprawidłowa nazwa komputera"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Nazwa komputera nie posiada podstawowej domeny"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Brak wystarczającej liczby domen"
diff --git a/po/pt.po b/po/pt.po
new file mode 100644
index 00000000..7db149fb
--- /dev/null
+++ b/po/pt.po
@@ -0,0 +1,105 @@
+# libsoup's Portuguese translation.
+# Copyright © 2012, 2013 libsoup
+# This file is distributed under the same license as the libsoup package.
+# Duarte Loreto <happyguy_pt@hotmail.com>, 2012, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: 3.8\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-03-11 22:54+0000\n"
+"PO-Revision-Date: 2013-03-11 23:00+0100\n"
+"Last-Translator: Duarte Loreto <happyguy_pt@hotmail.com>\n"
+"Language-Team: Portuguese <gnome_pt@yahoogroups.com>\n"
+"Language: pt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Ligação terminou inesperadamente"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Pedido de procura inválido"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Incapaz de truncar SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Fluxo de rede terminado inesperadamente"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Falha ao colocar o recurso totalmente em cache"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Buffer de resultado é demasiado pequeno"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Incapaz de processar a resposta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Codificação de resposta HTTP desconhecida"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "A operação foi cancelada"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "A operação iria bloquear"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Incapaz de processar o pedido HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Nenhum URI especificado"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "URI '%s' inválido: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Incapaz de processar o URI '%s'"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Esquema de URI '%s' não suportado"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Não é um URI HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Nome da máquina é um endereço IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Nome de máquina inválido"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Nome de máquina não possui domínio base"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Domínios insuficientes"
diff --git a/po/pt_BR.po b/po/pt_BR.po
new file mode 100644
index 00000000..c567838b
--- /dev/null
+++ b/po/pt_BR.po
@@ -0,0 +1,108 @@
+# Brazilian Portuguese translation for libsoup.
+# Copyright (C) 2013 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Gabriel F. Vilar <cogumm@gmail.com>, 2012.
+# Enrico Nicoletto <liverig@gmail.com>, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-03-06 19:50+0000\n"
+"PO-Revision-Date: 2013-02-28 10:19-0300\n"
+"Last-Translator: Enrico Nicoletto <liverig@gmail.com>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "A conexão terminou inesperadamente"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Requisição de busca inválida"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Não foi possível truncar SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "O fluxo de rede fechou de forma inesperada"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Falha ao armazenar completamente em cache o recurso"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "O buffer de saída é muito pequeno"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Não foi possível analisar a resposta HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Codificação de resposta HTTP não reconhecível"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "A operação foi cancelada"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "A operação será bloqueada"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Não foi possível analisar a solicitação HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Nenhuma URI foi fornecida"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "URI: %s inválida '%s'"
+
+#: ../libsoup/soup-session.c:4208
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Não foi possível analisar URI '%s'"
+
+#: ../libsoup/soup-session.c:4245
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Esquema de URI não suportado '%s'"
+
+#: ../libsoup/soup-session.c:4267
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Não é um URI do tipo HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "O nome da máquina é um endereço de IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "O nome da máquina é inválido"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Nome da máquina não está na base do domínio"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Não há domínios suficientes"
diff --git a/po/ru.po b/po/ru.po
index abb606b1..375b8bc0 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -1,54 +1,109 @@
-# Russian translation for libsoup.
-# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
-# This file is distributed under the same license as the libsoup package.
-# Yuri Myasoedov <omerta13@yandex.ru>, 2012.
+# Russian translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# Yuri Myasoedov <omerta13@yandex.ru>, 2012, 2013. , 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
-"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-04-22 08:40+0000\n"
-"PO-Revision-Date: 2012-04-23 12:43+0300\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-03-15 11:36+0000\n"
+"PO-Revision-Date: 2013-03-16 22:47+0400\n"
"Last-Translator: Yuri Myasoedov <omerta13@yandex.ru>\n"
-"Language-Team: Russian <gnome-cyr@gnome.org>\n"
+"Language-Team: русский <gnome-cyr@gnome.org>\n"
+"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203
-#: ../libsoup/soup-message-io.c:232
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Соединение было неожиданно разорвано"
-#: ../libsoup/soup-converter-wrapper.c:185
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Неверный запрос поиска"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Не удалось отсечь SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Сетевой поток неожиданно закрылся"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Не удалось полностью закэшировать ресурс"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Слишком маленький буфер вывода"
-#: ../libsoup/soup-message-io.c:818
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Не удалось разобрать HTTP-ответ"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Нераспознанная кодировка HTTP-ответа"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Действие отменено"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Действие заблокировано"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Не удалось разобрать HTTP-запрос"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Не указан URI"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Недопустимый URI «%s»: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4252
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Не удалось разобрать URI «%s»"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4289
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Неподдерживаемая схема URI «%s»"
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Формат URI отличается от HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Имя компьютера является IP-адресом"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Неверное имя компьютера"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Имя компьютера не содержит доменной части"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Недостаточно доменных имён в адресе"
diff --git a/po/sk.po b/po/sk.po
new file mode 100644
index 00000000..cb442ca0
--- /dev/null
+++ b/po/sk.po
@@ -0,0 +1,114 @@
+# Slovak translation for libsoup.
+# Copyright (C) 2012-2013 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Pavol Klačanský <pavol@klacansky.com>, 2012-2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-20 09:40+0000\n"
+"PO-Revision-Date: 2013-02-20 20:57+0000\n"
+"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
+"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
+"Language: sk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Pripojenie bolo neočakávane ukončené"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Neplatná požiadavka na posunutie"
+
+# struct
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream sa nedá skrátiť"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Sieťový prúd bol neočakávane uzavretý"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Úplné načítanie zdroja do dočasnej pamäte zlyhalo"
+
+# PM: buffer by som preložil ako schránka
+# PK: schranka je clipboard, buffer je jednoznacne vyrovnavacia pamet
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Výstupná vyrovnávacia pamäť je príliš malá"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Nepodarilo sa analyzovať odpoveď HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Nerozpoznané kódovanie odpovede HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Operácia bola zrušená"
+
+# PK: tu neviem ako to povedat, malo by ist o to, ze proste ta operacia neni async
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Operácia by blokovala spracovanie"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Nepodarilo sa analyzovať požiadavku HTTP"
+
+# error
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Nebol poskytnutý identifikátor URI"
+
+# first %s - scheme (http, ftp, ...)
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Neplatná schéma „%s“ identifikátora URI: %s"
+
+#: ../libsoup/soup-session.c:4211
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Nepodarilo sa analyzovať identifikátor URI „%s“"
+
+#: ../libsoup/soup-session.c:4248
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Nepodporovaná schéma „%s“ pre identifikátor URI"
+
+#: ../libsoup/soup-session.c:4270
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Nie je HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Názov hostiteľa je adresa IP"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Neplatný názov hostiteľa"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Názov hostiteľa neobsahuje základnú doménu"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Nedostatočný počet domén"
diff --git a/po/sl.po b/po/sl.po
index b38e1b0c..483f0337 100644
--- a/po/sl.po
+++ b/po/sl.po
@@ -7,75 +7,105 @@
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
-"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-07-18 21:44+0000\n"
-"PO-Revision-Date: 2012-07-19 09:57+0100\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-03-07 15:08+0000\n"
+"PO-Revision-Date: 2013-03-07 17:00+0100\n"
"Last-Translator: Matej Urbančič <mateju@svn.gnome.org>\n"
"Language-Team: Slovenian <gnome-si@googlegroups.com>\n"
"Language: sl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0);\n"
-"X-Poedit-Language: Slovenian\n"
-"X-Poedit-Country: SLOVENIA\n"
+"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n"
+"%100==4 ? 3 : 0);\n"
"X-Poedit-SourceCharset: utf-8\n"
+"X-Generator: Poedit 1.5.4\n"
-#: ../libsoup/soup-body-input-stream.c:136
-#: ../libsoup/soup-body-input-stream.c:167
-#: ../libsoup/soup-body-input-stream.c:200
-#: ../libsoup/soup-message-io.c:191
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Povezava je nepričakovano končana"
-#: ../libsoup/soup-converter-wrapper.c:189
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Neveljavna zahteva iskanja"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Ni mogoče porezati SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Omrežni pretok se je nepričakovano zaprl"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Ustvarjanje predpomnilnika vira je spodletelo"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Odvodni medpomnilnik je premajhen."
-#: ../libsoup/soup-message-io.c:817
-#: ../libsoup/soup-message-io.c:853
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Odziva HTTP ni mogoče razčleniti"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Neprepoznano kodiranje odziva HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Opravilo je preklicano."
-#: ../libsoup/soup-message-io.c:864
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Opravilo bi zaustavilo delovanje"
-#: ../libsoup/soup-request.c:142
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Zahteve HTTP ni mogoče razčleniti"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Ni podanega naslova URI"
-#: ../libsoup/soup-request.c:152
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Neveljaven naslov URI '%s': %s"
-#: ../libsoup/soup-requester.c:219
+#: ../libsoup/soup-session.c:4252
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Ni mogoče razčleniti naslova URI '%s'"
-#: ../libsoup/soup-requester.c:253
+#: ../libsoup/soup-session.c:4289
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Nepodprta shema URI '%s'"
-#: ../libsoup/soup-tld.c:148
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Naslov ni v obliki HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Ime gostitelja je naslov IP"
-#: ../libsoup/soup-tld.c:169
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Neveljavno ime gostitelja"
-#: ../libsoup/soup-tld.c:184
-#: ../libsoup/soup-tld.c:226
-msgid "Not enough domains"
-msgstr "Ni dovolj domen"
-
-#: ../libsoup/soup-tld.c:204
+#: ../libsoup/soup-tld.c:235
msgid "Hostname has no base domain"
msgstr "Ime gostitelja je brez osnovne domene"
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Ni dovolj domen"
diff --git a/po/sr.po b/po/sr.po
index 152ab0d1..885af1d3 100644
--- a/po/sr.po
+++ b/po/sr.po
@@ -1,74 +1,108 @@
# Serbian translation of libsoup.
# Courtesy of Prevod.org team (http://prevod.org/) -- 2012.
# This file is distributed under the same license as the libsoup package.
-# Мирослав Николић <miroslavnikolic@rocketmail.com>, 2011.
+# Мирослав Николић <miroslavnikolic@rocketmail.com>, 2011, 2012, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
-"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
-"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-07-17 14:16+0000\n"
-"PO-Revision-Date: 2011-03-05 22:03+0200\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsou"
+"p&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-02-18 20:11+0200\n"
"Last-Translator: Мирослав Николић <miroslavnikolic@rocketmail.com>\n"
"Language-Team: Serbian <gnom@prevod.org>\n"
+"Language: sr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: Serbian (sr)\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Project-Style: gnome\n"
-#: ../libsoup/soup-body-input-stream.c:136
-#: ../libsoup/soup-body-input-stream.c:167
-#: ../libsoup/soup-body-input-stream.c:200 ../libsoup/soup-message-io.c:191
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Веза је неочекивано затворена"
-#: ../libsoup/soup-converter-wrapper.c:189
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Неисправан захтев претраге"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Не могу да скратим улазни ток тела Супе"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Мрежни ток је изненадно затворен"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Нисам успео у потпуности да сместим извориште у оставу "
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Међумеморија излаза је премала"
-#: ../libsoup/soup-message-io.c:817 ../libsoup/soup-message-io.c:853
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Не могу да обрадим ХТТП одговор"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Непознато кодирање ХТТП одговора"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Радња је отказана"
-#: ../libsoup/soup-message-io.c:864
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Поступак би блокирао"
-#: ../libsoup/soup-request.c:142
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Не могу да обрадим ХТТП захтев"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Није наведена адреса"
-#: ../libsoup/soup-request.c:152
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Неисправна „%s“ адреса: %s"
-#: ../libsoup/soup-requester.c:219
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Не могу да обрадим адресу „%s“"
-#: ../libsoup/soup-requester.c:253
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Неподржана „%s“ шема адресе"
-#: ../libsoup/soup-tld.c:148
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Није ХТТП путања"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Назив домаћина је ИП адреса"
-#: ../libsoup/soup-tld.c:169
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Неисправан назив домаћина"
-#: ../libsoup/soup-tld.c:184 ../libsoup/soup-tld.c:226
-msgid "Not enough domains"
-msgstr "Нема довољно домена"
-
-#: ../libsoup/soup-tld.c:204
+#: ../libsoup/soup-tld.c:235
msgid "Hostname has no base domain"
msgstr "Назив домаћина нема основни домен"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Нема довољно домена"
diff --git a/po/sr@latin.po b/po/sr@latin.po
index 8fdeef4e..04d8dc0b 100644
--- a/po/sr@latin.po
+++ b/po/sr@latin.po
@@ -1,74 +1,108 @@
# Serbian translation of libsoup.
# Courtesy of Prevod.org team (http://prevod.org/) -- 2012.
# This file is distributed under the same license as the libsoup package.
-# Miroslav Nikolić <miroslavnikolic@rocketmail.com>, 2011.
+# Miroslav Nikolić <miroslavnikolic@rocketmail.com>, 2011, 2012, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
-"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
-"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-07-17 14:16+0000\n"
-"PO-Revision-Date: 2011-03-05 22:03+0200\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsou"
+"p&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-02-18 20:11+0200\n"
"Last-Translator: Miroslav Nikolić <miroslavnikolic@rocketmail.com>\n"
"Language-Team: Serbian <gnom@prevod.org>\n"
+"Language: sr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: Serbian (sr)\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Project-Style: gnome\n"
-#: ../libsoup/soup-body-input-stream.c:136
-#: ../libsoup/soup-body-input-stream.c:167
-#: ../libsoup/soup-body-input-stream.c:200 ../libsoup/soup-message-io.c:191
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Veza je neočekivano zatvorena"
-#: ../libsoup/soup-converter-wrapper.c:189
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Neispravan zahtev pretrage"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Ne mogu da skratim ulazni tok tela Supe"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Mrežni tok je iznenadno zatvoren"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Nisam uspeo u potpunosti da smestim izvorište u ostavu "
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Međumemorija izlaza je premala"
-#: ../libsoup/soup-message-io.c:817 ../libsoup/soup-message-io.c:853
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Ne mogu da obradim HTTP odgovor"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Nepoznato kodiranje HTTP odgovora"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Radnja je otkazana"
-#: ../libsoup/soup-message-io.c:864
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Postupak bi blokirao"
-#: ../libsoup/soup-request.c:142
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Ne mogu da obradim HTTP zahtev"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Nije navedena adresa"
-#: ../libsoup/soup-request.c:152
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "Neispravna „%s“ adresa: %s"
-#: ../libsoup/soup-requester.c:219
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Ne mogu da obradim adresu „%s“"
-#: ../libsoup/soup-requester.c:253
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Nepodržana „%s“ šema adrese"
-#: ../libsoup/soup-tld.c:148
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Nije HTTP putanja"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Naziv domaćina je IP adresa"
-#: ../libsoup/soup-tld.c:169
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Neispravan naziv domaćina"
-#: ../libsoup/soup-tld.c:184 ../libsoup/soup-tld.c:226
-msgid "Not enough domains"
-msgstr "Nema dovoljno domena"
-
-#: ../libsoup/soup-tld.c:204
+#: ../libsoup/soup-tld.c:235
msgid "Hostname has no base domain"
msgstr "Naziv domaćina nema osnovni domen"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Nema dovoljno domena"
diff --git a/po/sv.po b/po/sv.po
new file mode 100644
index 00000000..364abfa1
--- /dev/null
+++ b/po/sv.po
@@ -0,0 +1,75 @@
+# Swedish translation for libsoup.
+# Copyright (C) 2012 Free Software Foundation, Inc.
+# This file is distributed under the same license as the libsoup package.
+# Daniel Nylander <po@danielnylander.se>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-09-02 12:00+0200\n"
+"PO-Revision-Date: 2012-09-02 12:02+0100\n"
+"Last-Translator: Daniel Nylander <po@danielnylander.se>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../libsoup/soup-body-input-stream.c:136
+#: ../libsoup/soup-body-input-stream.c:167
+#: ../libsoup/soup-body-input-stream.c:200
+#: ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Anslutningen avslutades oväntat"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Utmatningsbufferten är för liten"
+
+#: ../libsoup/soup-message-io.c:818
+#: ../libsoup/soup-message-io.c:854
+msgid "Operation was cancelled"
+msgstr "Åtgärden avbröts"
+
+#: ../libsoup/soup-message-io.c:865
+msgid "Operation would block"
+msgstr "Åtgärden skulle blockera"
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "Ingen URI angavs"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Ogiltig \"%s\"-URI: %s"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Kunde inte tolka URI:n \"%s\""
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "URI-schemat \"%s\" stöds inte"
+
+#: ../libsoup/soup-tld.c:154
+msgid "Hostname is an IP address"
+msgstr "Värdnamnet är en IP-adress"
+
+#: ../libsoup/soup-tld.c:175
+msgid "Invalid hostname"
+msgstr "Ogiltigt värdnamn"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "Värdnamnet har ingen basdomän"
+
+#: ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "Inte tillräckligt många domäner"
+
diff --git a/po/ta.po b/po/ta.po
new file mode 100644
index 00000000..96b82ab4
--- /dev/null
+++ b/po/ta.po
@@ -0,0 +1,112 @@
+# Tamil translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# Dr.T.Vasudevan <agnihot3@gmail.com>, 2012.
+# Shantha kumar <shkumar@redhat.com>, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug."
+"cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-25 10:44+0530\n"
+"Last-Translator: Shantha kumar <shkumar@redhat.com>\n"
+"Language-Team: Tamil <>\n"
+"Language: ta\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Lokalize 1.0\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "இணைப்பு எதிர்பாராமல் துண்டிக்கப்பட்டது"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "செல்லுபடியாகாத தேடல் கோரிக்கை"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream ஐ பிரிக்க இயலாது"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "பிணைய ஸ்ட்ரீம் எதிர்பாராமல் முடிந்துவிட்டது"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "வளத்தை முழுவதுமாக தேக்ககப்படுத்துவதில் தோல்வியடைந்தது"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "வெளியீட்டு இடைநினைவு மிகச்சிறியது"
+
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "HTTP பதிலை பாகுபடுத்த முடியவில்லை"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "அடையாளம் காண முடியாத HTTP பதிலளிப்பு குறியீடாக்கம்"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "செயல்பாடு ரத்து செய்யப்பட்டது"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "செயல்பாடு தடை செய்யும்"
+
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "HTTP கோரிக்கையை பாகுபடுத்த முடியவில்லை"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "யூஆர்ஐ ஏதும் தரப்படவில்லை"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "செல்லுபடியாகாத '%s' URI: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "'%s' யூஆர்ஐ ஐ அலகிட முடியவில்லை"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "ஆதரவு இல்லாத யூஆர்ஐ திட்டம் '%s'"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI அல்ல"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "புரவலன் பெயர் ஒரு ஐபி முகவரி"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "செல்லுபடியாகாத கணிணிப்பெயர்"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "புரவலன் பெயருக்கு ஒரு செயற்களம் இல்லை"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "போதிய செயற்களம் இல்லை"
+
diff --git a/po/te.po b/po/te.po
index b76cef2a..4b2fed78 100644
--- a/po/te.po
+++ b/po/te.po
@@ -1,68 +1,112 @@
# Telugu translation for libsoup.
# Copyright (C) 2012 libsoup's to Swecha Telugu localisation Team <localization@swecha.net>
# This file is distributed under the same license as the libsoup package.
-# Sasi Bhushan Boddepalli <sasi@swecha.net>, 2012.
#
+# Sasi Bhushan Boddepalli <sasi@swecha.net>, 2012.
+# Krishnababu Krothapalli <kkrothap@redhat.com>, 2012, 2013.
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-06-18 13:31+0000\n"
-"PO-Revision-Date: 2012-06-18 13:31+0000\n"
-"Last-Translator: Sasi Bhushan Boddepalli <sasi@swecha.net>\n"
-"Language-Team: Telugu <indlinux-telugu@lists.sourceforge.net>\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-25 14:39+0530\n"
+"Last-Translator: Krishnababu Krothapalli <kkrothap@redhat.com>\n"
+"Language-Team: Telugu <Fedora-trans-te@redhat.com>\n"
+"Language: te\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Lokalize 1.5\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "కనెక్షను అనుకోకుండా అంతమైంది"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "చెల్లని సీక్ అభ్యర్ధన"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream ట్రంకేట్ చేయలేదు"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "నెట్వర్కు స్ట్రీమ్ అనుకోకుండా మూయబడింది"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "వనరును సంపూర్ణంగా క్యాచీ చేయుటకు విఫలమైంది"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "అవుట్పుట్ బఫర్ మరీ చిన్నది"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP response"
+msgstr "HTTP స్పందనను పార్శ్ చేయలేక పోయింది"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "గుర్తించని HTTP స్పందన యెన్కోడింగ్"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "ఆపరేషన్ రద్దు చేయబడింది"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "ఆపరేషన్ బ్లాక్ చేస్తుంది"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+#| msgid "Could not parse URI '%s'"
+msgid "Could not parse HTTP request"
+msgstr "HTTP అభ్యర్ధనను పార్స్ చేయలేదు"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "URl అందించబడలేదు"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "చెల్లని '%s' URI: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "URI '%s' అన్వయించడం సాధ్యం కాదు"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "మద్దతు లేని URI స్కీమ్ '%s'"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI కాదు"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "హోస్ట్ పేరు ఒక IP చిరునామా"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "చెల్లని హోస్ట్ పేరు"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "అతిధేయిపేరు అధార డొమైన్ కలిగిలేదు"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "సరిపోనన్ని డొమైన్ లు"
+
diff --git a/po/tg.po b/po/tg.po
new file mode 100644
index 00000000..c8332dd7
--- /dev/null
+++ b/po/tg.po
@@ -0,0 +1,107 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Victor Ibragimov <victor.ibragimov@gmail.com>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Tajik Gnome\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-07-28 11:48+0000\n"
+"PO-Revision-Date: 2013-10-10 16:30+0500\n"
+"Last-Translator: Victor Ibragimov <victor.ibragimov@gmail.com>\n"
+"Language-Team: \n"
+"Language: Tajik\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.7\n"
+
+#: ../libsoup/soup-body-input-stream.c:140
+#: ../libsoup/soup-body-input-stream.c:171
+#: ../libsoup/soup-body-input-stream.c:204 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Пайвастшавӣ ногаҳон қатъ шудааст"
+
+#: ../libsoup/soup-body-input-stream.c:462
+msgid "Invalid seek request"
+msgstr "Дархости ҷустуҷӯи нодуруст"
+
+#: ../libsoup/soup-body-input-stream.c:490
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream кӯтоҳ карда намешавад"
+
+#: ../libsoup/soup-cache-input-stream.c:73
+msgid "Network stream unexpectedly closed"
+msgstr "Ҷараёни шабака ногаҳон манъ карда шудааст"
+
+#: ../libsoup/soup-cache-input-stream.c:290
+msgid "Failed to completely cache the resource"
+msgstr "Зерҳофизаи манбаъ комилан захира карда нашуд"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Миёнҷии барориш хеле хурд аст"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Ҷавоби HTTP таҷзия карда намешавад"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Рамзгузории ҷавоби HTTP шинохта нашудааст"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "Амалиёт бекор шудааст"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "Амалиёт қатъ карда мешавад"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Дархости HTTP таҷзия карда намешавад"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "Ягон URI таъмин нашудааст"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Суроғаи '%s' URI нодуруст аст: %s"
+
+#: ../libsoup/soup-session.c:4301
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Суроғаи URI '%s' таҷзия карда нашуд"
+
+#: ../libsoup/soup-session.c:4338
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Нақшаи суроғаи URI '%s' дастгирӣ намешавад"
+
+#: ../libsoup/soup-session.c:4360
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Суроғаи HTTP URI намебошад"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Номи мизбон суроғаи IP мебошад"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Номи мизбони нодуруст"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Номи мизбон домени асосӣ надорад"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Доменҳои кофӣ надорад"
diff --git a/po/th.po b/po/th.po
new file mode 100644
index 00000000..b2c806a0
--- /dev/null
+++ b/po/th.po
@@ -0,0 +1,107 @@
+# Thai translation for libsoup.
+# Copyright (C) 2012-2013 Free Software Foundation, Inc.
+# This file is distributed under the same license as the libsoup package.
+# Akom Chotiphantawanon <knight2000@gmail.com>, 2012.
+# Theppitak Karoonboonyanan <thep@linux.thai.net>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-09 11:38+0700\n"
+"Last-Translator: Theppitak Karoonboonyanan <thep@linux.thai.net>\n"
+"Language-Team: Thai <thai-l10n@googlegroups.com>\n"
+"Language: th\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "การเชื่อมต่อยุติกะทันหัน"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "การร้องขอการเลื่อนตำแหน่งไม่ถูกต้อง"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "ไม่สามารถตัดท้าย SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "สตรีมเครือข่ายปิดกะทันหัน"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "ทำแคชทรัพยากรไม่เสร็จสมบูรณ์"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "บัฟเฟอร์ข้อมูลออกเล็กเกินไป"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "ไม่สามารถแจงคำตอบ HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "ไม่รู้จักรหัสอักขระของคำตอบ HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "การทำงานถูกยกเลิก"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "การทำงานถูกบล็อค"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "ไม่สามารถแจงคำร้อง HTTP"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "ไม่ได้ระบุ URI"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "URI '%s' ไม่ถูกต้อง: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "ไม่สามารถแจง URI '%s'"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "ไม่รองรับ URI แบบ '%s'"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "ไม่ใช่ URI ของ HTTP"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "ชื่อโฮสต์เป็นหมายเลขไอพี"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "ชื่อโฮสต์ผิดรูปแบบ"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "ชื่อโฮสต์ไม่มีโดเมนฐาน"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "มีโดเมนน้อยเกินไป"
diff --git a/po/tr.po b/po/tr.po
new file mode 100644
index 00000000..809ebf54
--- /dev/null
+++ b/po/tr.po
@@ -0,0 +1,110 @@
+# Turkish translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+#
+# Muhammet Kara <muhammetk@acikkaynak.name.tr>, 2012.
+# Ozan Çağlayan <ozancag@gmail.com>, 2013.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-04-03 16:00+0000\n"
+"PO-Revision-Date: 2013-04-07 22:53+0300\n"
+"Last-Translator: Ozan Çağlayan <ozancag@gmail.com>\n"
+"Language-Team: Türkçe <>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Gtranslator 2.91.6\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Bağlantı beklenmeyen bir şekilde sonlandı"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Geçersiz arama talebi"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStream kesilemiyor"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Ağ akışı beklenmeyen bir şekilde sonlandı"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Özkaynak tamamen önbelleğe alınamadı"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Çıkış arabelleği çok küçük"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "HTTP cevabı ayrıştırılamadı"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "HTTP cevabı tanınmayan bir şekilde kodlanmış"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "İşlem iptal edildi"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "İşlem bloke edebilir"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "HTTP talebi ayrıştırılamadı"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "URI verilmedi"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Geçersiz '%s' URI: %s"
+
+#: ../libsoup/soup-session.c:4252
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s' ayrıştırılamadı"
+
+#: ../libsoup/soup-session.c:4289
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Desteklenmeyen URI şeması '%s'"
+
+#: ../libsoup/soup-session.c:4311
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "HTTP URI değil"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "Makine adı bir IP adresi"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "Geçersiz makine adı"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Ana makinenin temel etki alanı yok"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "Yeterli etki alanı yok"
diff --git a/po/ug.po b/po/ug.po
new file mode 100644
index 00000000..c5c366a6
--- /dev/null
+++ b/po/ug.po
@@ -0,0 +1,105 @@
+# Uyghur translation for libsoup.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Gheyret Kenji <gheyret@gmail.com>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-02-22 21:51+0900\n"
+"Last-Translator: Gheyret Kenji <gheyret@gmail.com>\n"
+"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "باغلىنىش تۇيۇقسىز توختاپ قالدى"
+
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "ئىناۋەتسىز ئىزدەش ئىلتىماسى"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "ئېقىم SoupBodyInputStream نى قىسقارتقىلى بولمىدى"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "تور ئېقىمى تۇيۇقسىز يېپىلدى"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "مەنبەنى تولۇق غەملىۋېلىش مەغلۇپ بولدى"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "چىقىرىش يىغلەكى بەك كىچىك"
+
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "HTTP ئىنكاسىنى يېشەلمىدى"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "HTTP ئىنكاسىنىڭ كودلىنىشىنى بىلگىلى بولمىدى"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
+msgid "Operation was cancelled"
+msgstr "مەشغۇلات بىكار قىلىندى"
+
+#: ../libsoup/soup-message-io.c:893
+msgid "Operation would block"
+msgstr "مەشغۇلات توسۇلىدۇ"
+
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "HTTP ئىلتىماسىنى يېشەلمىدى"
+
+#: ../libsoup/soup-request.c:140
+#, c-format
+msgid "No URI provided"
+msgstr "URI تەمىنلەنمىدى"
+
+#: ../libsoup/soup-request.c:150
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "ئىناۋەتسىز ‹%s› URI: %s"
+
+#: ../libsoup/soup-session.c:4209
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI «%s» نى تەھلىل قىلالمىدى"
+
+#: ../libsoup/soup-session.c:4246
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "قوللىمايدىغان URI لايىھە ‹%s›"
+
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "بۇ HTTP URI ئەمەس"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "كومپيۇتېر ئاتى دېگەن بىر دانە IP ئادرېستۇر"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "باش ماشىنا ئاتى ئىناۋەتسىز"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "كومپيۇتېر ئاتىدا ئاساسىي دائىرە يوق"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "يېتەرلىك دائىرە يوق"
diff --git a/po/uk.po b/po/uk.po
new file mode 100644
index 00000000..7f17ca6f
--- /dev/null
+++ b/po/uk.po
@@ -0,0 +1,84 @@
+# Ukrainian translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Daniel <ted.korostiled@gmail.com>, 2012.
+# Re. <ted.korostiled@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup master\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-09-21 10:06+0300\n"
+"PO-Revision-Date: 2012-09-21 10:09+0300\n"
+"Last-Translator: Re. <ted.korostiled@gmail.com>\n"
+"Language-Team: linux.org.ua\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%"
+"10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Virtaal 0.7.1\n"
+"X-Project-Style: gnome\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Несподівано зв'язок розірвано"
+
+#: ../libsoup/soup-body-input-stream.c:461
+msgid "Invalid seek request"
+msgstr "Неправильний запит пошуку"
+
+#: ../libsoup/soup-body-input-stream.c:489
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Неможливо вкоротити SoupBodyInputStream"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Вихідний буфер надто малий"
+
+#: ../libsoup/soup-message-io.c:818 ../libsoup/soup-message-io.c:854
+msgid "Operation was cancelled"
+msgstr "Операцію скасовано"
+
+#: ../libsoup/soup-message-io.c:865
+msgid "Operation would block"
+msgstr "Операція заблокує"
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "Не надано URI"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Неправильний «%s» URI: %s"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "Неможливо розібрати URI «%s»"
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Непідтримувана схема URI «%s»"
+
+#: ../libsoup/soup-tld.c:154
+msgid "Hostname is an IP address"
+msgstr "Назва вузла — адреса IP"
+
+#: ../libsoup/soup-tld.c:175
+msgid "Invalid hostname"
+msgstr "Неправильна назва вузла"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "Назва вузла не містить базового домену"
+
+#: ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "Не вистачає доменів"
diff --git a/po/uz@cyrillic.po b/po/uz@cyrillic.po
new file mode 100644
index 00000000..7060d2f1
--- /dev/null
+++ b/po/uz@cyrillic.po
@@ -0,0 +1,82 @@
+# Uzbek (Cyrillic) translation for libsoup.
+# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libsoup package.
+# Bahodir Mansurov <6ahodir@gmail.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libsoup gnome-3-6\n"
+"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
+"product=libsoup&keywords=I18N+L10N&component=general\n"
+"POT-Creation-Date: 2012-10-16 12:52+0000\n"
+"PO-Revision-Date: 2012-10-16 14:46-0500\n"
+"Last-Translator: Bahodir Mansurov <6ahodir@gmail.com>\n"
+"Language-Team: Uzbek (Cyrillic) <uz@cyrillic@li.org>\n"
+"Language: uz@cyrillic\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
+msgid "Connection terminated unexpectedly"
+msgstr "Алоқа кутилмаганда узилди"
+
+#: ../libsoup/soup-body-input-stream.c:461
+msgid "Invalid seek request"
+msgstr "Нотўғри излаш сўрови"
+
+#: ../libsoup/soup-body-input-stream.c:489
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "SoupBodyInputStreamни тўхтатиб бўлмади"
+
+#: ../libsoup/soup-converter-wrapper.c:192
+#, c-format
+msgid "Output buffer is too small"
+msgstr "Чиқув буфери жуда кичик"
+
+#: ../libsoup/soup-message-io.c:818 ../libsoup/soup-message-io.c:854
+msgid "Operation was cancelled"
+msgstr "Амал инкор этилди"
+
+#: ../libsoup/soup-message-io.c:865
+msgid "Operation would block"
+msgstr "Амал тўсиб қўяди"
+
+#: ../libsoup/soup-request.c:142
+#, c-format
+msgid "No URI provided"
+msgstr "URI кўрсатилмаган"
+
+#: ../libsoup/soup-request.c:152
+#, c-format
+msgid "Invalid '%s' URI: %s"
+msgstr "Нотўғри URI '%s': '%s'"
+
+#: ../libsoup/soup-requester.c:219
+#, c-format
+msgid "Could not parse URI '%s'"
+msgstr "URI '%s' таҳлил қилиб бўлмади"
+
+#: ../libsoup/soup-requester.c:253
+#, c-format
+msgid "Unsupported URI scheme '%s'"
+msgstr "Қўллаб қувватланмаган '%s' URI чизмаси"
+
+#: ../libsoup/soup-tld.c:154
+msgid "Hostname is an IP address"
+msgstr "Компьютер номи IP адресдан иборат"
+
+#: ../libsoup/soup-tld.c:175
+msgid "Invalid hostname"
+msgstr "Нотўғри компьютер номи"
+
+#: ../libsoup/soup-tld.c:204
+msgid "Hostname has no base domain"
+msgstr "Компьютер номида домен қисми йўқ"
+
+#: ../libsoup/soup-tld.c:226
+msgid "Not enough domains"
+msgstr "Домен исмлари етарли емас"
diff --git a/po/vi.po b/po/vi.po
index 5fc62da4..f10402e2 100644
--- a/po/vi.po
+++ b/po/vi.po
@@ -1,69 +1,107 @@
# Vietnamese translation for libsoup.
# Copyright (C) 2012 libsoup's COPYRIGHT HOLDER
# This file is distributed under the same license as the libsoup package.
-# Nguyễn Thái Ngọc Duy <pclouds@gmail.com>, 2012.
+# Nguyễn Thái Ngọc Duy <pclouds@gmail.com>, 2012-2013.
#
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-06-28 03:27+0000\n"
-"PO-Revision-Date: 2012-06-30 10:15+0700\n"
+"POT-Creation-Date: 2013-02-18 10:20+0000\n"
+"PO-Revision-Date: 2013-03-02 20:44+0700\n"
"Last-Translator: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n"
"Language-Team: Vietnamese <gnomevi-list@lists.sourceforge.net>\n"
+"Language: vi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "Kết nối ngắt bất ngờ"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "Yêu cầu di chuyển (seek) không hợp lệ"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "Không thể cắt SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "Luồng mạng đóng bất ngờ"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "Lỗi lưu tạm (cache) toàn bộ tài nguyên"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "Vùng đệm xuất quá nhỏ"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "Không thể phân tích phản hồi HTTP"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "Không nhận ra bảng mã phản hồi HTTP"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "Thao tác bị huỷ"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "Thao tác có thể treo"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "Không thể phân tích phản hồi HTTP"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "Chưa có URI"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "URI '%s' không hợp lệ: %s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4209
#, c-format
msgid "Could not parse URI '%s'"
msgstr "Không thể phân tích URI '%s'"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4246
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "Không hỗ trợ kiểu URI '%s'"
-#: ../libsoup/soup-tld.c:145
+#: ../libsoup/soup-session.c:4268
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "Không phải HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "Tên máy là địa chỉ IP"
-#: ../libsoup/soup-tld.c:166
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "Tên máy không hợp lệ"
-#: ../libsoup/soup-tld.c:181 ../libsoup/soup-tld.c:219
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "Tên máy không có miền cơ bản"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "Không đủ miền"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 9c94b06a..3f746aa0 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -3,55 +3,109 @@
# This file is distributed under the same license as the libsoup package.
# hmasterwang <hmasterwang@gmail.com>, 2012.
# Alpha Cheng <hmasterwang@gmail.com>, 2012.
+# Cheng Lu <chenglu1990@gmail.com>, 2012.
+# YunQiang Su <wzssyqa@gmail.com>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=libsoup&keywords=I18N+L10N&component=general\n"
-"POT-Creation-Date: 2012-05-24 20:15+0000\n"
-"PO-Revision-Date: 2012-05-22 17:55+0800\n"
-"Last-Translator: Wylmer Wang <wantinghard@gmail.com>\n"
-"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
+"POT-Creation-Date: 2013-11-11 22:47+0000\n"
+"PO-Revision-Date: 2013-09-07 14:07+0700\n"
+"Last-Translator: 甘露(Gan Lu) <rhythm.gan@gmail.com>\n"
+"Language-Team: Chinese (simplified) <i18n-zh@googlegroups.com>\n"
+"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 1.5.4\n"
-#: ../libsoup/soup-body-input-stream.c:139
-#: ../libsoup/soup-body-input-stream.c:170
-#: ../libsoup/soup-body-input-stream.c:203 ../libsoup/soup-message-io.c:231
+#: ../libsoup/soup-body-input-stream.c:140
+#: ../libsoup/soup-body-input-stream.c:171
+#: ../libsoup/soup-body-input-stream.c:204 ../libsoup/soup-message-io.c:197
msgid "Connection terminated unexpectedly"
msgstr "连接异常终止"
-#: ../libsoup/soup-converter-wrapper.c:190
+#: ../libsoup/soup-body-input-stream.c:462
+msgid "Invalid seek request"
+msgstr "无效的 seek 请求"
+
+#: ../libsoup/soup-body-input-stream.c:490
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "不能截断(truncate) SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:73
+msgid "Network stream unexpectedly closed"
+msgstr "网络流意外关闭"
+
+#: ../libsoup/soup-cache-input-stream.c:290
+msgid "Failed to completely cache the resource"
+msgstr "无法完全缓存该资源"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "输出缓冲区太小"
-#: ../libsoup/soup-message-io.c:836 ../libsoup/soup-message-io.c:863
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "无法解析 HTTP 响应"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "未识别的 HTTP 响应编码"
+
+#: ../libsoup/soup-message-io.c:855 ../libsoup/soup-message-io.c:891
msgid "Operation was cancelled"
msgstr "操作被取消"
-#: ../libsoup/soup-message-io.c:874
+#: ../libsoup/soup-message-io.c:902
msgid "Operation would block"
msgstr "操作将阻塞"
-#: ../libsoup/soup-request.c:145
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "无法解析 HTTP 请求"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "未提供 URI"
-#: ../libsoup/soup-request.c:155
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "无效的“%s”URI:%s"
-#: ../libsoup/soup-requester.c:220
+#: ../libsoup/soup-session.c:4302
#, c-format
msgid "Could not parse URI '%s'"
msgstr "无法解析 URI:“%s”"
-#: ../libsoup/soup-requester.c:254
+#: ../libsoup/soup-session.c:4339
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "不支持的 URI 方案:“%s”"
+
+#: ../libsoup/soup-session.c:4361
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "不是一个 HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
+msgid "Hostname is an IP address"
+msgstr "主机名是一个 IP 地址"
+
+#: ../libsoup/soup-tld.c:206
+msgid "Invalid hostname"
+msgstr "主机名无效"
+
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "主机名没有基域"
+
+#: ../libsoup/soup-tld.c:257
+msgid "Not enough domains"
+msgstr "没有足够的域"
diff --git a/po/zh_HK.po b/po/zh_HK.po
index 817d6d65..f9e17ba7 100644
--- a/po/zh_HK.po
+++ b/po/zh_HK.po
@@ -7,62 +7,100 @@ msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-16 08:47+0800\n"
-"PO-Revision-Date: 2012-07-16 08:48+0800\n"
+"POT-Creation-Date: 2013-03-01 22:41+0800\n"
+"PO-Revision-Date: 2013-03-01 22:41+0800\n"
"Last-Translator: Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>\n"
"Language-Team: Chinese (Hong Kong) <community@linuxhall.org>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
-#: ../libsoup/soup-body-input-stream.c:136
-#: ../libsoup/soup-body-input-stream.c:167
-#: ../libsoup/soup-body-input-stream.c:200 ../libsoup/soup-message-io.c:193
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "連線無預警的關閉了"
-#: ../libsoup/soup-converter-wrapper.c:189
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "無效的搜尋要求"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "不能截短 SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "網絡串流無預警的關閉了"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "無法完整快取資源"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "輸出緩衝區太小"
-#: ../libsoup/soup-message-io.c:817 ../libsoup/soup-message-io.c:853
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "無法解析 HTTP 回應"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "未辨識的 HTTP 回應編碼"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "操作已被取消"
-#: ../libsoup/soup-message-io.c:864
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "操作會阻擋"
-#: ../libsoup/soup-request.c:142
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "無法解析 HTTP 要求"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "未提供 URI"
-#: ../libsoup/soup-request.c:152
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "無效的「%s」URI:%s"
-#: ../libsoup/soup-requester.c:219
+#: ../libsoup/soup-session.c:4211
#, c-format
msgid "Could not parse URI '%s'"
msgstr "無法解析 URI「%s」"
-#: ../libsoup/soup-requester.c:253
+#: ../libsoup/soup-session.c:4248
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "不支援的 URI scheme「%s」"
-#: ../libsoup/soup-tld.c:146
+#: ../libsoup/soup-session.c:4270
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "並非 HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "主機名稱是 IP 位址"
-#: ../libsoup/soup-tld.c:167
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "主機名稱無效"
-#: ../libsoup/soup-tld.c:182 ../libsoup/soup-tld.c:220
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "主機名稱沒有基礎網域"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "沒有足夠的網域"
diff --git a/po/zh_TW.po b/po/zh_TW.po
index f71178d8..d59a81ca 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -7,62 +7,100 @@ msgid ""
msgstr ""
"Project-Id-Version: libsoup master\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-16 08:47+0800\n"
-"PO-Revision-Date: 2012-07-14 11:47+0800\n"
+"POT-Creation-Date: 2013-03-01 22:41+0800\n"
+"PO-Revision-Date: 2013-02-28 09:48+0800\n"
"Last-Translator: Chao-Hsiung Liao <j_h_liau@yahoo.com.tw>\n"
"Language-Team: Chinese (Taiwan) <chinese-l10n@googlegroups.com>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
-#: ../libsoup/soup-body-input-stream.c:136
-#: ../libsoup/soup-body-input-stream.c:167
-#: ../libsoup/soup-body-input-stream.c:200 ../libsoup/soup-message-io.c:193
+#: ../libsoup/soup-body-input-stream.c:141
+#: ../libsoup/soup-body-input-stream.c:172
+#: ../libsoup/soup-body-input-stream.c:205 ../libsoup/soup-message-io.c:191
msgid "Connection terminated unexpectedly"
msgstr "連線無預警的關閉了"
-#: ../libsoup/soup-converter-wrapper.c:189
+#: ../libsoup/soup-body-input-stream.c:463
+msgid "Invalid seek request"
+msgstr "無效的搜尋要求"
+
+#: ../libsoup/soup-body-input-stream.c:491
+msgid "Cannot truncate SoupBodyInputStream"
+msgstr "不能截短 SoupBodyInputStream"
+
+#: ../libsoup/soup-cache-input-stream.c:74
+msgid "Network stream unexpectedly closed"
+msgstr "網路串流無預警的關閉了"
+
+#: ../libsoup/soup-cache-input-stream.c:291
+msgid "Failed to completely cache the resource"
+msgstr "無法完整快取資源"
+
+#: ../libsoup/soup-converter-wrapper.c:192
#, c-format
msgid "Output buffer is too small"
msgstr "輸出緩衝區太小"
-#: ../libsoup/soup-message-io.c:817 ../libsoup/soup-message-io.c:853
+#: ../libsoup/soup-message-client-io.c:41
+msgid "Could not parse HTTP response"
+msgstr "無法解析 HTTP 回應"
+
+#: ../libsoup/soup-message-client-io.c:66
+msgid "Unrecognized HTTP response encoding"
+msgstr "未辨識的 HTTP 回應編碼"
+
+#: ../libsoup/soup-message-io.c:846 ../libsoup/soup-message-io.c:882
msgid "Operation was cancelled"
msgstr "操作已被取消"
-#: ../libsoup/soup-message-io.c:864
+#: ../libsoup/soup-message-io.c:893
msgid "Operation would block"
msgstr "操作會阻擋"
-#: ../libsoup/soup-request.c:142
+#: ../libsoup/soup-message-server-io.c:40
+msgid "Could not parse HTTP request"
+msgstr "無法解析 HTTP 要求"
+
+#: ../libsoup/soup-request.c:140
#, c-format
msgid "No URI provided"
msgstr "未提供 URI"
-#: ../libsoup/soup-request.c:152
+#: ../libsoup/soup-request.c:150
#, c-format
msgid "Invalid '%s' URI: %s"
msgstr "無效的「%s」URI:%s"
-#: ../libsoup/soup-requester.c:219
+#: ../libsoup/soup-session.c:4211
#, c-format
msgid "Could not parse URI '%s'"
msgstr "無法解析 URI「%s」"
-#: ../libsoup/soup-requester.c:253
+#: ../libsoup/soup-session.c:4248
#, c-format
msgid "Unsupported URI scheme '%s'"
msgstr "不支援的 URI scheme「%s」"
-#: ../libsoup/soup-tld.c:146
+#: ../libsoup/soup-session.c:4270
+#, c-format
+msgid "Not an HTTP URI"
+msgstr "並非 HTTP URI"
+
+#: ../libsoup/soup-tld.c:185
msgid "Hostname is an IP address"
msgstr "主機名稱是 IP 位址"
-#: ../libsoup/soup-tld.c:167
+#: ../libsoup/soup-tld.c:206
msgid "Invalid hostname"
msgstr "主機名稱無效"
-#: ../libsoup/soup-tld.c:182 ../libsoup/soup-tld.c:220
+#: ../libsoup/soup-tld.c:235
+msgid "Hostname has no base domain"
+msgstr "主機名稱沒有基礎網域"
+
+#: ../libsoup/soup-tld.c:257
msgid "Not enough domains"
msgstr "沒有足夠的網域"
diff --git a/tap-driver.sh b/tap-driver.sh
new file mode 100755
index 00000000..19aa531d
--- /dev/null
+++ b/tap-driver.sh
@@ -0,0 +1,652 @@
+#! /bin/sh
+# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+scriptversion=2011-12-27.17; # UTC
+
+# Make unconditional expansion of undefined variables an error. This
+# helps a lot in preventing typo-related bugs.
+set -u
+
+me=tap-driver.sh
+
+fatal ()
+{
+ echo "$me: fatal: $*" >&2
+ exit 1
+}
+
+usage_error ()
+{
+ echo "$me: $*" >&2
+ print_usage >&2
+ exit 2
+}
+
+print_usage ()
+{
+ cat <<END
+Usage:
+ tap-driver.sh --test-name=NAME --log-file=PATH --trs-file=PATH
+ [--expect-failure={yes|no}] [--color-tests={yes|no}]
+ [--enable-hard-errors={yes|no}] [--ignore-exit]
+ [--diagnostic-string=STRING] [--merge|--no-merge]
+ [--comments|--no-comments] [--] TEST-COMMAND
+The \`--test-name', \`--log-file' and \`--trs-file' options are mandatory.
+END
+}
+
+# TODO: better error handling in option parsing (in particular, ensure
+# TODO: $log_file, $trs_file and $test_name are defined).
+test_name= # Used for reporting.
+log_file= # Where to save the result and output of the test script.
+trs_file= # Where to save the metadata of the test run.
+expect_failure=0
+color_tests=0
+merge=0
+ignore_exit=0
+comments=0
+diag_string='#'
+while test $# -gt 0; do
+ case $1 in
+ --help) print_usage; exit $?;;
+ --version) echo "$me $scriptversion"; exit $?;;
+ --test-name) test_name=$2; shift;;
+ --log-file) log_file=$2; shift;;
+ --trs-file) trs_file=$2; shift;;
+ --color-tests) color_tests=$2; shift;;
+ --expect-failure) expect_failure=$2; shift;;
+ --enable-hard-errors) shift;; # No-op.
+ --merge) merge=1;;
+ --no-merge) merge=0;;
+ --ignore-exit) ignore_exit=1;;
+ --comments) comments=1;;
+ --no-comments) comments=0;;
+ --diagnostic-string) diag_string=$2; shift;;
+ --) shift; break;;
+ -*) usage_error "invalid option: '$1'";;
+ esac
+ shift
+done
+
+test $# -gt 0 || usage_error "missing test command"
+
+case $expect_failure in
+ yes) expect_failure=1;;
+ *) expect_failure=0;;
+esac
+
+if test $color_tests = yes; then
+ init_colors='
+ color_map["red"]="" # Red.
+ color_map["grn"]="" # Green.
+ color_map["lgn"]="" # Light green.
+ color_map["blu"]="" # Blue.
+ color_map["mgn"]="" # Magenta.
+ color_map["std"]="" # No color.
+ color_for_result["ERROR"] = "mgn"
+ color_for_result["PASS"] = "grn"
+ color_for_result["XPASS"] = "red"
+ color_for_result["FAIL"] = "red"
+ color_for_result["XFAIL"] = "lgn"
+ color_for_result["SKIP"] = "blu"'
+else
+ init_colors=''
+fi
+
+# :; is there to work around a bug in bash 3.2 (and earlier) which
+# does not always set '$?' properly on redirection failure.
+# See the Autoconf manual for more details.
+:;{
+ (
+ # Ignore common signals (in this subshell only!), to avoid potential
+ # problems with Korn shells. Some Korn shells are known to propagate
+ # to themselves signals that have killed a child process they were
+ # waiting for; this is done at least for SIGINT (and usually only for
+ # it, in truth). Without the `trap' below, such a behaviour could
+ # cause a premature exit in the current subshell, e.g., in case the
+ # test command it runs gets terminated by a SIGINT. Thus, the awk
+ # script we are piping into would never seen the exit status it
+ # expects on its last input line (which is displayed below by the
+ # last `echo $?' statement), and would thus die reporting an internal
+ # error.
+ # For more information, see the Autoconf manual and the threads:
+ # <http://lists.gnu.org/archive/html/bug-autoconf/2011-09/msg00004.html>
+ # <http://mail.opensolaris.org/pipermail/ksh93-integration-discuss/2009-February/004121.html>
+ trap : 1 3 2 13 15
+ if test $merge -gt 0; then
+ exec 2>&1
+ else
+ exec 2>&3
+ fi
+ "$@"
+ echo $?
+ ) | LC_ALL=C ${AM_TAP_AWK-awk} \
+ -v me="$me" \
+ -v test_script_name="$test_name" \
+ -v log_file="$log_file" \
+ -v trs_file="$trs_file" \
+ -v expect_failure="$expect_failure" \
+ -v merge="$merge" \
+ -v ignore_exit="$ignore_exit" \
+ -v comments="$comments" \
+ -v diag_string="$diag_string" \
+'
+# FIXME: the usages of "cat >&3" below could be optimized when using
+# FIXME: GNU awk, and/on on systems that supports /dev/fd/.
+
+# Implementation note: in what follows, `result_obj` will be an
+# associative array that (partly) simulates a TAP result object
+# from the `TAP::Parser` perl module.
+
+## ----------- ##
+## FUNCTIONS ##
+## ----------- ##
+
+function fatal(msg)
+{
+ print me ": " msg | "cat >&2"
+ exit 1
+}
+
+function abort(where)
+{
+ fatal("internal error " where)
+}
+
+# Convert a boolean to a "yes"/"no" string.
+function yn(bool)
+{
+ return bool ? "yes" : "no";
+}
+
+function add_test_result(result)
+{
+ if (!test_results_index)
+ test_results_index = 0
+ test_results_list[test_results_index] = result
+ test_results_index += 1
+ test_results_seen[result] = 1;
+}
+
+# Whether the test script should be re-run by "make recheck".
+function must_recheck()
+{
+ for (k in test_results_seen)
+ if (k != "XFAIL" && k != "PASS" && k != "SKIP")
+ return 1
+ return 0
+}
+
+# Whether the content of the log file associated to this test should
+# be copied into the "global" test-suite.log.
+function copy_in_global_log()
+{
+ for (k in test_results_seen)
+ if (k != "PASS")
+ return 1
+ return 0
+}
+
+# FIXME: this can certainly be improved ...
+function get_global_test_result()
+{
+ if ("ERROR" in test_results_seen)
+ return "ERROR"
+ if ("FAIL" in test_results_seen || "XPASS" in test_results_seen)
+ return "FAIL"
+ all_skipped = 1
+ for (k in test_results_seen)
+ if (k != "SKIP")
+ all_skipped = 0
+ if (all_skipped)
+ return "SKIP"
+ return "PASS";
+}
+
+function stringify_result_obj(result_obj)
+{
+ if (result_obj["is_unplanned"] || result_obj["number"] != testno)
+ return "ERROR"
+
+ if (plan_seen == LATE_PLAN)
+ return "ERROR"
+
+ if (result_obj["directive"] == "TODO")
+ return result_obj["is_ok"] ? "XPASS" : "XFAIL"
+
+ if (result_obj["directive"] == "SKIP")
+ return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL;
+
+ if (length(result_obj["directive"]))
+ abort("in function stringify_result_obj()")
+
+ return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL
+}
+
+function decorate_result(result)
+{
+ color_name = color_for_result[result]
+ if (color_name)
+ return color_map[color_name] "" result "" color_map["std"]
+ # If we are not using colorized output, or if we do not know how
+ # to colorize the given result, we should return it unchanged.
+ return result
+}
+
+function report(result, details)
+{
+ if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/)
+ {
+ msg = ": " test_script_name
+ add_test_result(result)
+ }
+ else if (result == "#")
+ {
+ msg = " " test_script_name ":"
+ }
+ else
+ {
+ abort("in function report()")
+ }
+ if (length(details))
+ msg = msg " " details
+ # Output on console might be colorized.
+ print decorate_result(result) msg
+ # Log the result in the log file too, to help debugging (this is
+ # especially true when said result is a TAP error or "Bail out!").
+ print result msg | "cat >&3";
+}
+
+function testsuite_error(error_message)
+{
+ report("ERROR", "- " error_message)
+}
+
+function handle_tap_result()
+{
+ details = result_obj["number"];
+ if (length(result_obj["description"]))
+ details = details " " result_obj["description"]
+
+ if (plan_seen == LATE_PLAN)
+ {
+ details = details " # AFTER LATE PLAN";
+ }
+ else if (result_obj["is_unplanned"])
+ {
+ details = details " # UNPLANNED";
+ }
+ else if (result_obj["number"] != testno)
+ {
+ details = sprintf("%s # OUT-OF-ORDER (expecting %d)",
+ details, testno);
+ }
+ else if (result_obj["directive"])
+ {
+ details = details " # " result_obj["directive"];
+ if (length(result_obj["explanation"]))
+ details = details " " result_obj["explanation"]
+ }
+
+ report(stringify_result_obj(result_obj), details)
+}
+
+# `skip_reason` should be empty whenever planned > 0.
+function handle_tap_plan(planned, skip_reason)
+{
+ planned += 0 # Avoid getting confused if, say, `planned` is "00"
+ if (length(skip_reason) && planned > 0)
+ abort("in function handle_tap_plan()")
+ if (plan_seen)
+ {
+ # Error, only one plan per stream is acceptable.
+ testsuite_error("multiple test plans")
+ return;
+ }
+ planned_tests = planned
+ # The TAP plan can come before or after *all* the TAP results; we speak
+ # respectively of an "early" or a "late" plan. If we see the plan line
+ # after at least one TAP result has been seen, assume we have a late
+ # plan; in this case, any further test result seen after the plan will
+ # be flagged as an error.
+ plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN)
+ # If testno > 0, we have an error ("too many tests run") that will be
+ # automatically dealt with later, so do not worry about it here. If
+ # $plan_seen is true, we have an error due to a repeated plan, and that
+ # has already been dealt with above. Otherwise, we have a valid "plan
+ # with SKIP" specification, and should report it as a particular kind
+ # of SKIP result.
+ if (planned == 0 && testno == 0)
+ {
+ if (length(skip_reason))
+ skip_reason = "- " skip_reason;
+ report("SKIP", skip_reason);
+ }
+}
+
+function extract_tap_comment(line)
+{
+ if (index(line, diag_string) == 1)
+ {
+ # Strip leading `diag_string` from `line`.
+ line = substr(line, length(diag_string) + 1)
+ # And strip any leading and trailing whitespace left.
+ sub("^[ \t]*", "", line)
+ sub("[ \t]*$", "", line)
+ # Return what is left (if any).
+ return line;
+ }
+ return "";
+}
+
+# When this function is called, we know that line is a TAP result line,
+# so that it matches the (perl) RE "^(not )?ok\b".
+function setup_result_obj(line)
+{
+ # Get the result, and remove it from the line.
+ result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0)
+ sub("^(not )?ok[ \t]*", "", line)
+
+ # If the result has an explicit number, get it and strip it; otherwise,
+ # automatically assing the next progresive number to it.
+ if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/)
+ {
+ match(line, "^[0-9]+")
+ # The final `+ 0` is to normalize numbers with leading zeros.
+ result_obj["number"] = substr(line, 1, RLENGTH) + 0
+ line = substr(line, RLENGTH + 1)
+ }
+ else
+ {
+ result_obj["number"] = testno
+ }
+
+ if (plan_seen == LATE_PLAN)
+ # No further test results are acceptable after a "late" TAP plan
+ # has been seen.
+ result_obj["is_unplanned"] = 1
+ else if (plan_seen && testno > planned_tests)
+ result_obj["is_unplanned"] = 1
+ else
+ result_obj["is_unplanned"] = 0
+
+ # Strip trailing and leading whitespace.
+ sub("^[ \t]*", "", line)
+ sub("[ \t]*$", "", line)
+
+ # This will have to be corrected if we have a "TODO"/"SKIP" directive.
+ result_obj["description"] = line
+ result_obj["directive"] = ""
+ result_obj["explanation"] = ""
+
+ if (index(line, "#") == 0)
+ return # No possible directive, nothing more to do.
+
+ # Directives are case-insensitive.
+ rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*"
+
+ # See whether we have the directive, and if yes, where.
+ pos = match(line, rx "$")
+ if (!pos)
+ pos = match(line, rx "[^a-zA-Z0-9_]")
+
+ # If there was no TAP directive, we have nothing more to do.
+ if (!pos)
+ return
+
+ # Let`s now see if the TAP directive has been escaped. For example:
+ # escaped: ok \# SKIP
+ # not escaped: ok \\# SKIP
+ # escaped: ok \\\\\# SKIP
+ # not escaped: ok \ # SKIP
+ if (substr(line, pos, 1) == "#")
+ {
+ bslash_count = 0
+ for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--)
+ bslash_count += 1
+ if (bslash_count % 2)
+ return # Directive was escaped.
+ }
+
+ # Strip the directive and its explanation (if any) from the test
+ # description.
+ result_obj["description"] = substr(line, 1, pos - 1)
+ # Now remove the test description from the line, that has been dealt
+ # with already.
+ line = substr(line, pos)
+ # Strip the directive, and save its value (normalized to upper case).
+ sub("^[ \t]*#[ \t]*", "", line)
+ result_obj["directive"] = toupper(substr(line, 1, 4))
+ line = substr(line, 5)
+ # Now get the explanation for the directive (if any), with leading
+ # and trailing whitespace removed.
+ sub("^[ \t]*", "", line)
+ sub("[ \t]*$", "", line)
+ result_obj["explanation"] = line
+}
+
+function get_test_exit_message(status)
+{
+ if (status == 0)
+ return ""
+ if (status !~ /^[1-9][0-9]*$/)
+ abort("getting exit status")
+ if (status < 127)
+ exit_details = ""
+ else if (status == 127)
+ exit_details = " (command not found?)"
+ else if (status >= 128 && status <= 255)
+ exit_details = sprintf(" (terminated by signal %d?)", status - 128)
+ else if (status > 256 && status <= 384)
+ # We used to report an "abnormal termination" here, but some Korn
+ # shells, when a child process die due to signal number n, can leave
+ # in $? an exit status of 256+n instead of the more standard 128+n.
+ # Apparently, both behaviours are allowed by POSIX (2008), so be
+ # prepared to handle them both. See also Austing Group report ID
+ # 0000051 <http://www.austingroupbugs.net/view.php?id=51>
+ exit_details = sprintf(" (terminated by signal %d?)", status - 256)
+ else
+ # Never seen in practice.
+ exit_details = " (abnormal termination)"
+ return sprintf("exited with status %d%s", status, exit_details)
+}
+
+function write_test_results()
+{
+ print ":global-test-result: " get_global_test_result() > trs_file
+ print ":recheck: " yn(must_recheck()) > trs_file
+ print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file
+ for (i = 0; i < test_results_index; i += 1)
+ print ":test-result: " test_results_list[i] > trs_file
+ close(trs_file);
+}
+
+BEGIN {
+
+## ------- ##
+## SETUP ##
+## ------- ##
+
+'"$init_colors"'
+
+# Properly initialized once the TAP plan is seen.
+planned_tests = 0
+
+COOKED_PASS = expect_failure ? "XPASS": "PASS";
+COOKED_FAIL = expect_failure ? "XFAIL": "FAIL";
+
+# Enumeration-like constants to remember which kind of plan (if any)
+# has been seen. It is important that NO_PLAN evaluates "false" as
+# a boolean.
+NO_PLAN = 0
+EARLY_PLAN = 1
+LATE_PLAN = 2
+
+testno = 0 # Number of test results seen so far.
+bailed_out = 0 # Whether a "Bail out!" directive has been seen.
+
+# Whether the TAP plan has been seen or not, and if yes, which kind
+# it is ("early" is seen before any test result, "late" otherwise).
+plan_seen = NO_PLAN
+
+## --------- ##
+## PARSING ##
+## --------- ##
+
+is_first_read = 1
+
+while (1)
+ {
+ # Involutions required so that we are able to read the exit status
+ # from the last input line.
+ st = getline
+ if (st < 0) # I/O error.
+ fatal("I/O error while reading from input stream")
+ else if (st == 0) # End-of-input
+ {
+ if (is_first_read)
+ abort("in input loop: only one input line")
+ break
+ }
+ if (is_first_read)
+ {
+ is_first_read = 0
+ nextline = $0
+ continue
+ }
+ else
+ {
+ curline = nextline
+ nextline = $0
+ $0 = curline
+ }
+ # Copy any input line verbatim into the log file.
+ print | "cat >&3"
+ # Parsing of TAP input should stop after a "Bail out!" directive.
+ if (bailed_out)
+ continue
+
+ # TAP test result.
+ if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/)
+ {
+ testno += 1
+ setup_result_obj($0)
+ handle_tap_result()
+ }
+ # TAP plan (normal or "SKIP" without explanation).
+ else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/)
+ {
+ # The next two lines will put the number of planned tests in $0.
+ sub("^1\\.\\.", "")
+ sub("[^0-9]*$", "")
+ handle_tap_plan($0, "")
+ continue
+ }
+ # TAP "SKIP" plan, with an explanation.
+ else if ($0 ~ /^1\.\.0+[ \t]*#/)
+ {
+ # The next lines will put the skip explanation in $0, stripping
+ # any leading and trailing whitespace. This is a little more
+ # tricky in truth, since we want to also strip a potential leading
+ # "SKIP" string from the message.
+ sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "")
+ sub("[ \t]*$", "");
+ handle_tap_plan(0, $0)
+ }
+ # "Bail out!" magic.
+ # Older versions of prove and TAP::Harness (e.g., 3.17) did not
+ # recognize a "Bail out!" directive when preceded by leading
+ # whitespace, but more modern versions (e.g., 3.23) do. So we
+ # emulate the latter, "more modern" behaviour.
+ else if ($0 ~ /^[ \t]*Bail out!/)
+ {
+ bailed_out = 1
+ # Get the bailout message (if any), with leading and trailing
+ # whitespace stripped. The message remains stored in `$0`.
+ sub("^[ \t]*Bail out![ \t]*", "");
+ sub("[ \t]*$", "");
+ # Format the error message for the
+ bailout_message = "Bail out!"
+ if (length($0))
+ bailout_message = bailout_message " " $0
+ testsuite_error(bailout_message)
+ }
+ # Maybe we have too look for dianogtic comments too.
+ else if (comments != 0)
+ {
+ comment = extract_tap_comment($0);
+ if (length(comment))
+ report("#", comment);
+ }
+ }
+
+## -------- ##
+## FINISH ##
+## -------- ##
+
+# A "Bail out!" directive should cause us to ignore any following TAP
+# error, as well as a non-zero exit status from the TAP producer.
+if (!bailed_out)
+ {
+ if (!plan_seen)
+ {
+ testsuite_error("missing test plan")
+ }
+ else if (planned_tests != testno)
+ {
+ bad_amount = testno > planned_tests ? "many" : "few"
+ testsuite_error(sprintf("too %s tests run (expected %d, got %d)",
+ bad_amount, planned_tests, testno))
+ }
+ if (!ignore_exit)
+ {
+ # Fetch exit status from the last line.
+ exit_message = get_test_exit_message(nextline)
+ if (exit_message)
+ testsuite_error(exit_message)
+ }
+ }
+
+write_test_results()
+
+exit 0
+
+} # End of "BEGIN" block.
+'
+
+# TODO: document that we consume the file descriptor 3 :-(
+} 3>"$log_file"
+
+test $? -eq 0 || fatal "I/O or internal error"
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/tap-test b/tap-test
new file mode 100755
index 00000000..481e333e
--- /dev/null
+++ b/tap-test
@@ -0,0 +1,5 @@
+#! /bin/sh
+
+# run a GTest in tap mode. The test binary is passed as $1
+
+$1 -k --tap
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 995196c3..a8b9d019 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,143 +1,136 @@
-INCLUDES = \
+include $(top_srcdir)/glib-tap.mk
+
+AM_CPPFLAGS = \
-I$(top_srcdir) \
- -DSRCDIR=\""$(abs_srcdir)"\" \
- -DLIBSOUP_DISABLE_DEPRECATED \
- $(SOUP_MAINTAINER_FLAGS) \
- $(XML_CFLAGS) \
$(GLIB_CFLAGS)
LIBS = \
$(top_builddir)/libsoup/libsoup-2.4.la \
- $(LIBGNUTLS_LIBS) \
$(GLIB_LIBS)
-noinst_PROGRAMS = \
- chunk-test \
- coding-test \
- connection-test \
- context-test \
- continue-test \
- cookies-test \
- date \
- dns \
- forms-test \
- get \
- header-parsing \
- misc-test \
- ntlm-test \
- redirect-test \
- requester-test \
- simple-httpd \
- simple-proxy \
- sniffing-test \
- ssl-test \
- streaming-test \
- timeout-test \
- tld-test \
- uri-parsing \
- $(CURL_TESTS) \
- $(APACHE_TESTS) \
- $(XMLRPC_TESTS)
-
-TEST_SRCS = test-utils.c test-utils.h
-
-auth_test_SOURCES = auth-test.c $(TEST_SRCS)
-chunk_test_SOURCES = chunk-test.c $(TEST_SRCS)
-coding_test_SOURCES = coding-test.c $(TEST_SRCS)
-connection_test_SOURCES = connection-test.c $(TEST_SRCS)
-context_test_SOURCES = context-test.c $(TEST_SRCS)
-continue_test_SOURCES = continue-test.c $(TEST_SRCS)
-cookies_test_SOURCES = cookies-test.c $(TEST_SRCS)
-date_SOURCES = date.c $(TEST_SRCS)
-dns_SOURCES = dns.c
-forms_test_SOURCES = forms-test.c $(TEST_SRCS)
-get_SOURCES = get.c
-if BUILD_LIBSOUP_GNOME
-get_LDADD = $(top_builddir)/libsoup/libsoup-gnome-2.4.la
-endif
-header_parsing_SOURCES = header-parsing.c $(TEST_SRCS)
-misc_test_SOURCES = misc-test.c $(TEST_SRCS)
-ntlm_test_SOURCES = ntlm-test.c $(TEST_SRCS)
-proxy_test_SOURCES = proxy-test.c $(TEST_SRCS)
-pull_api_SOURCES = pull-api.c $(TEST_SRCS)
-range_test_SOURCES = range-test.c $(TEST_SRCS)
-redirect_test_SOURCES = redirect-test.c $(TEST_SRCS)
-requester_test_SOURCES = requester-test.c $(TEST_SRCS)
-server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS)
-simple_httpd_SOURCES = simple-httpd.c
-simple_proxy_SOURCES = simple-proxy.c
-sniffing_test_SOURCES = sniffing-test.c $(TEST_SRCS)
-ssl_test_SOURCES = ssl-test.c $(TEST_SRCS)
-streaming_test_SOURCES = streaming-test.c $(TEST_SRCS)
-timeout_test_SOURCES = timeout-test.c $(TEST_SRCS)
-tld_test_SOURCES = tld-test.c $(TEST_SRCS)
-uri_parsing_SOURCES = uri-parsing.c $(TEST_SRCS)
-xmlrpc_test_SOURCES = xmlrpc-test.c $(TEST_SRCS)
-xmlrpc_server_test_SOURCES = xmlrpc-server-test.c $(TEST_SRCS)
+test_programs = \
+ auth-test \
+ cache-test \
+ chunk-test \
+ chunk-io-test \
+ coding-test \
+ connection-test \
+ context-test \
+ continue-test \
+ cookies-test \
+ date \
+ forms-test \
+ header-parsing \
+ misc-test \
+ multipart-test \
+ no-ssl-test \
+ ntlm-test \
+ proxy-test \
+ pull-api \
+ range-test \
+ redirect-test \
+ requester-test \
+ resource-test \
+ session-test \
+ server-auth-test \
+ server-test \
+ sniffing-test \
+ socket-test \
+ ssl-test \
+ streaming-test \
+ timeout-test \
+ tld-test \
+ uri-parsing \
+ xmlrpc-server-test \
+ xmlrpc-test
+
+test_extra_programs = \
+ ntlm-test-helper \
+ $(TESTS)
+
+test_data = \
+ index.txt \
+ soup-tests.gresource \
+ test-cert.pem \
+ test-key.pem \
+ xmlrpc-server.php
+
+noinst_LTLIBRARIES += libtest.la
+
+libtest_la_SOURCES = \
+ test-utils.c \
+ test-utils.h
+
+LDADD = libtest.la
if HAVE_APACHE
-APACHE_TESTS = auth-test proxy-test pull-api range-test
-endif
-if HAVE_CURL
-CURL_TESTS = forms-test server-auth-test
-endif
-if HAVE_XMLRPC_EPI_PHP
-XMLRPC_TESTS = xmlrpc-test xmlrpc-server-test
+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 $@
-TESTS = \
- chunk-test \
- coding-test \
- connection-test \
- context-test \
- continue-test \
- cookies-test \
- date \
- header-parsing \
- misc-test \
- ntlm-test \
- redirect-test \
- requester-test \
- sniffing-test \
- ssl-test \
- streaming-test \
- timeout-test \
- tld-test \
- uri-parsing \
- $(APACHE_TESTS) \
- $(CURL_TESTS) \
- $(XMLRPC_TESTS)
-
-RESOURCES = \
- resources/atom.xml \
- resources/home.gif \
- resources/html_binary.html \
- resources/mbox \
- resources/mbox.gz \
- resources/mbox.raw \
- resources/mbox.zlib \
- resources/ps_binary.ps \
- resources/rss20.xml \
- resources/test.html \
- resources/text_binary.txt
-
-EXTRA_DIST = \
+BUILT_SOURCES += httpd.conf
+test_data += \
htdigest \
htpasswd \
- httpd.conf.in \
- index.txt \
- libsoup.supp \
- test-cert.pem \
- test-key.pem \
- xmlrpc-server.php \
+ httpd.conf
+endif
+
+RESOURCES = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/soup-tests.gresource.xml)
+
+soup-tests.gresource: soup-tests.gresource.xml $(RESOURCES)
+ $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) $<
+
+EXTRA_DIST += \
+ 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)
-if MISSING_REGRESSION_TEST_PACKAGES
+DISTCLEANFILES += soup-tests.gresource httpd.conf
+
+TESTS_ENVIRONMENT += SOUP_TESTS_IN_MAKE_CHECK=1
+
+check: start-httpd
+
check-local: check-TESTS
- @echo ""
- @echo "NOTE: some tests were not run due to missing packages:" $(MISSING_REGRESSION_TEST_PACKAGES)
- @echo ""
+ @$(MAKE) kill-httpd
+
+.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:
- $(APACHE_HTTPD) -d `pwd` -f httpd.conf -k stop
+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/auth-test.c b/tests/auth-test.c
index 85c4d414..8ed5cead 100644
--- a/tests/auth-test.c
+++ b/tests/auth-test.c
@@ -2,6 +2,7 @@
#include "test-utils.h"
+static const char *base_uri;
static GMainLoop *loop;
typedef struct {
@@ -35,10 +36,6 @@ typedef struct {
guint final_status;
} SoupAuthTest;
-/* Will either point to main_tests or relogin_tests
- */
-static SoupAuthTest *current_tests;
-
static SoupAuthTest main_tests[] = {
{ "No auth available, should fail",
"Basic/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED },
@@ -146,7 +143,12 @@ static SoupAuthTest main_tests[] = {
"Digest/realm1/not/", "1", FALSE, /* should not be used */ "1", SOUP_STATUS_UNAUTHORIZED },
{ "Make sure we've forgotten it",
- "Digest/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED }
+ "Digest/realm1/", "", FALSE, "0", SOUP_STATUS_UNAUTHORIZED },
+
+ { "Fail with URI-embedded password, then use right password in the authenticate signal",
+ "Basic/realm3/", "43", TRUE, "43", SOUP_STATUS_OK },
+
+ { NULL }
};
static const char *auths[] = {
@@ -202,14 +204,12 @@ handler (SoupMessage *msg, gpointer data)
if (*expected) {
exp = *expected - '0';
- if (auth != exp) {
- debug_printf (1, " expected %s!\n", auths[exp]);
- errors++;
- }
+ soup_test_assert (auth == exp,
+ "expected %s", auths[exp]);
memmove (expected, expected + 1, strlen (expected));
} else {
- debug_printf (1, " expected to be finished\n");
- errors++;
+ soup_test_assert (*expected,
+ "expected to be finished");
}
}
@@ -217,18 +217,18 @@ static void
authenticate (SoupSession *session, SoupMessage *msg,
SoupAuth *auth, gboolean retrying, gpointer data)
{
- int *i = data;
+ SoupAuthTest *test = data;
char *username, *password;
char num;
- if (!current_tests[*i].provided[0])
+ if (!test->provided[0])
return;
if (retrying) {
- if (!current_tests[*i].provided[1])
+ if (!test->provided[1])
return;
- num = current_tests[*i].provided[1];
+ num = test->provided[1];
} else
- num = current_tests[*i].provided[0];
+ num = test->provided[0];
username = g_strdup_printf ("user%c", num);
password = g_strdup_printf ("realm%c", num);
@@ -244,13 +244,10 @@ bug271540_sent (SoupMessage *msg, gpointer data)
gboolean *authenticated = data;
int auth = identify_auth (msg);
- if (!*authenticated && auth) {
- debug_printf (1, " using auth on message %d before authenticating!!??\n", n);
- errors++;
- } else if (*authenticated && !auth) {
- debug_printf (1, " sent unauthenticated message %d after authenticating!\n", n);
- errors++;
- }
+ soup_test_assert (*authenticated || !auth,
+ "using auth on message %d before authenticating", n);
+ soup_test_assert (!*authenticated || auth,
+ "sent unauthenticated message %d after authenticating", n);
}
static void
@@ -269,8 +266,8 @@ bug271540_authenticate (SoupSession *session, SoupMessage *msg,
soup_auth_authenticate (auth, "user1", "realm1");
*authenticated = TRUE;
} else {
- debug_printf (1, " asked to authenticate message %d after authenticating!\n", n);
- errors++;
+ soup_test_assert (!*authenticated,
+ "asked to authenticate message %d after authenticating", n);
}
}
@@ -278,13 +275,8 @@ static void
bug271540_finished (SoupSession *session, SoupMessage *msg, gpointer data)
{
int *left = data;
- int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#"));
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " got status '%d %s' on message %d!\n",
- msg->status_code, msg->reason_phrase, n);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
(*left)--;
if (!*left)
@@ -292,7 +284,7 @@ bug271540_finished (SoupSession *session, SoupMessage *msg, gpointer data)
}
static void
-do_pipelined_auth_test (const char *base_uri)
+do_pipelined_auth_test (void)
{
SoupSession *session;
SoupMessage *msg;
@@ -300,7 +292,10 @@ do_pipelined_auth_test (const char *base_uri)
char *uri;
int i;
- debug_printf (1, "Testing pipelined auth (bug 271540):\n");
+ g_test_bug ("271540");
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
authenticated = FALSE;
@@ -437,28 +432,21 @@ do_digest_nonce_test (SoupSession *session,
&got_401);
got_401 = FALSE;
soup_session_send_message (session, msg);
- if (got_401 != expect_401) {
- debug_printf (1, " %s request %s a 401 Unauthorized!\n", nth,
- got_401 ? "got" : "did not get");
- errors++;
- }
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " %s request got status %d %s!\n", nth,
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- if (errors == 0)
- debug_printf (1, " %s request succeeded\n", nth);
+ soup_test_assert (got_401 == expect_401,
+ "%s request %s a 401 Unauthorized!\n", nth,
+ got_401 ? "got" : "did not get");
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
g_object_unref (msg);
}
static void
-do_digest_expiration_test (const char *base_uri)
+do_digest_expiration_test (void)
{
SoupSession *session;
char *uri;
- debug_printf (1, "\nTesting digest nonce expiration:\n");
+ SOUP_TEST_SKIP_IF_NO_APACHE;
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -530,10 +518,8 @@ async_authenticate_assert_once (SoupSession *session, SoupMessage *msg,
debug_printf (2, " async_authenticate_assert_once\n");
- if (*been_here) {
- debug_printf (1, " ERROR: async_authenticate_assert_once called twice\n");
- errors++;
- }
+ soup_test_assert (!*been_here,
+ "async_authenticate_assert_once called twice");
*been_here = TRUE;
}
@@ -545,10 +531,8 @@ async_authenticate_assert_once_and_stop (SoupSession *session, SoupMessage *msg,
debug_printf (2, " async_authenticate_assert_once_and_stop\n");
- if (*been_here) {
- debug_printf (1, " ERROR: async_authenticate_assert_once called twice\n");
- errors++;
- }
+ soup_test_assert (!*been_here,
+ "async_authenticate_assert_once called twice");
*been_here = TRUE;
soup_session_pause_message (session, msg);
@@ -556,7 +540,7 @@ async_authenticate_assert_once_and_stop (SoupSession *session, SoupMessage *msg,
}
static void
-do_async_auth_test (const char *base_uri)
+do_async_auth_good_password_test (void)
{
SoupSession *session;
SoupMessage *msg1, *msg2, *msg3, msg2_bak;
@@ -564,15 +548,13 @@ do_async_auth_test (const char *base_uri)
char *uri;
SoupAuth *auth = NULL;
int remaining;
- gboolean been_there;
- debug_printf (1, "\nTesting async auth:\n");
+ SOUP_TEST_SKIP_IF_NO_APACHE;
loop = g_main_loop_new (NULL, TRUE);
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- remaining = 0;
-
uri = g_strconcat (base_uri, "Basic/realm1/", NULL);
+ remaining = 0;
msg1 = soup_message_new ("GET", uri);
g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
@@ -590,13 +572,7 @@ do_async_auth_test (const char *base_uri)
g_object_set_data (G_OBJECT (msg2), "id", GINT_TO_POINTER (2));
soup_session_send_message (session, msg2);
- if (msg2->status_code == SOUP_STATUS_UNAUTHORIZED)
- debug_printf (1, " msg2 failed as expected\n");
- else {
- debug_printf (1, " msg2 got wrong status! (%u)\n",
- msg2->status_code);
- errors++;
- }
+ soup_test_assert_message_status (msg2, SOUP_STATUS_UNAUTHORIZED);
/* msg2 should be done at this point; assuming everything is
* working correctly, the session won't look at it again; we
@@ -627,25 +603,11 @@ do_async_auth_test (const char *base_uri)
g_main_loop_run (loop);
/* async_finished will quit the loop */
- } else {
- debug_printf (1, " msg1 didn't get authenticate signal!\n");
- errors++;
- }
+ } else
+ soup_test_assert (auth, "msg1 didn't get authenticate signal");
- if (msg1->status_code == SOUP_STATUS_OK)
- debug_printf (1, " msg1 succeeded\n");
- else {
- debug_printf (1, " msg1 FAILED! (%u %s)\n",
- msg1->status_code, msg1->reason_phrase);
- errors++;
- }
- if (msg3->status_code == SOUP_STATUS_OK)
- debug_printf (1, " msg3 succeeded\n");
- else {
- debug_printf (1, " msg3 FAILED! (%u %s)\n",
- msg3->status_code, msg3->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg1, SOUP_STATUS_OK);
+ soup_test_assert_message_status (msg3, SOUP_STATUS_OK);
soup_test_session_abort_unref (session);
@@ -654,27 +616,46 @@ do_async_auth_test (const char *base_uri)
memcpy (msg2, &msg2_bak, sizeof (SoupMessage));
g_object_unref (msg2);
+ g_free (uri);
+ g_main_loop_unref (loop);
+}
+
+static void
+do_async_auth_bad_password_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ guint auth_id;
+ char *uri;
+ SoupAuth *auth = NULL;
+ int remaining;
+ gboolean been_there;
+
/* Test that giving the wrong password doesn't cause multiple
* authenticate signals the second time.
*/
- debug_printf (1, "\nTesting async auth with wrong password (#522601):\n");
+ g_test_bug ("522601");
+
+ 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);
remaining = 0;
auth = NULL;
- msg1 = soup_message_new ("GET", uri);
- g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
+ msg = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (1));
auth_id = g_signal_connect (session, "authenticate",
G_CALLBACK (async_authenticate), &auth);
- g_object_ref (msg1);
+ g_object_ref (msg);
remaining++;
- soup_session_queue_message (session, msg1, async_finished, &remaining);
+ soup_session_queue_message (session, msg, async_finished, &remaining);
g_main_loop_run (loop);
g_signal_handler_disconnect (session, auth_id);
soup_auth_authenticate (auth, "user1", "wrong");
g_object_unref (auth);
- soup_session_unpause_message (session, msg1);
+ soup_session_unpause_message (session, msg);
been_there = FALSE;
auth_id = g_signal_connect (session, "authenticate",
@@ -683,55 +664,71 @@ do_async_auth_test (const char *base_uri)
g_main_loop_run (loop);
g_signal_handler_disconnect (session, auth_id);
- if (!been_there) {
- debug_printf (1, " authenticate not emitted?\n");
- errors++;
- }
+ soup_test_assert (been_there,
+ "authenticate not emitted");
soup_test_session_abort_unref (session);
- g_object_unref (msg1);
+ g_object_unref (msg);
+
+ g_free (uri);
+ g_main_loop_unref (loop);
+}
+
+static void
+do_async_auth_no_password_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ guint auth_id;
+ char *uri;
+ int remaining;
+ gboolean been_there;
/* Test that giving no password doesn't cause multiple
* authenticate signals the second time.
*/
- debug_printf (1, "\nTesting async auth with no password (#583462):\n");
+ g_test_bug ("583462");
+
+ 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);
remaining = 0;
/* Send a message that doesn't actually authenticate
*/
- msg1 = soup_message_new ("GET", uri);
- g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
+ msg = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (1));
auth_id = g_signal_connect (session, "authenticate",
G_CALLBACK (async_authenticate), NULL);
- g_object_ref (msg1);
+ g_object_ref (msg);
remaining++;
- soup_session_queue_message (session, msg1, async_finished, &remaining);
+ soup_session_queue_message (session, msg, async_finished, &remaining);
g_main_loop_run (loop);
g_signal_handler_disconnect (session, auth_id);
- soup_session_unpause_message (session, msg1);
+ soup_session_unpause_message (session, msg);
g_main_loop_run (loop);
- g_object_unref(msg1);
+ g_object_unref(msg);
/* Now send a second message */
- msg1 = soup_message_new ("GET", uri);
- g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (2));
- g_object_ref (msg1);
+ msg = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (2));
+ g_object_ref (msg);
been_there = FALSE;
auth_id = g_signal_connect (session, "authenticate",
G_CALLBACK (async_authenticate_assert_once_and_stop),
&been_there);
remaining++;
- soup_session_queue_message (session, msg1, async_finished, &remaining);
+ soup_session_queue_message (session, msg, async_finished, &remaining);
g_main_loop_run (loop);
- soup_session_unpause_message (session, msg1);
+ soup_session_unpause_message (session, msg);
g_main_loop_run (loop);
g_signal_handler_disconnect (session, auth_id);
soup_test_session_abort_unref (session);
- g_object_unref (msg1);
+ g_object_unref (msg);
g_free (uri);
g_main_loop_unref (loop);
@@ -795,41 +792,27 @@ select_auth_test_one (SoupURI *uri,
msg = soup_message_new_from_uri ("GET", uri);
soup_session_send_message (session, msg);
- if (strcmp (sad.round[0].headers, first_headers) != 0) {
- debug_printf (1, " Header order wrong: expected %s, got %s\n",
- first_headers, sad.round[0].headers);
- errors++;
- }
- if (strcmp (sad.round[0].response, first_response) != 0) {
- debug_printf (1, " Selected auth type wrong: expected %s, got %s\n",
- first_response, sad.round[0].response);
- errors++;
- }
-
- if (second_headers && !sad.round[1].headers) {
- debug_printf (1, " Expected a second round!\n");
- errors++;
- } else if (!second_headers && sad.round[1].headers) {
- debug_printf (1, " Didn't expect a second round!\n");
- errors++;
- } else if (second_headers) {
- if (strcmp (sad.round[1].headers, second_headers) != 0) {
- debug_printf (1, " Second round header order wrong: expected %s, got %s\n",
- second_headers, sad.round[1].headers);
- errors++;
- }
- if (strcmp (sad.round[1].response, second_response) != 0) {
- debug_printf (1, " Second round selected auth type wrong: expected %s, got %s\n",
- second_response, sad.round[1].response);
- errors++;
- }
+ soup_test_assert (strcmp (sad.round[0].headers, first_headers) == 0,
+ "Header order wrong: expected %s, got %s",
+ first_headers, sad.round[0].headers);
+ soup_test_assert (strcmp (sad.round[0].response, first_response) == 0,
+ "Selected auth type wrong: expected %s, got %s",
+ first_response, sad.round[0].response);
+
+ soup_test_assert (sad.round[1].headers || !second_headers,
+ "Expected a second round");
+ soup_test_assert (!sad.round[1].headers || second_headers,
+ "Didn't expect a second round");
+ if (second_headers && second_response) {
+ soup_test_assert (strcmp (sad.round[1].headers, second_headers) == 0,
+ "Second round header order wrong: expected %s, got %s\n",
+ second_headers, sad.round[1].headers);
+ soup_test_assert (strcmp (sad.round[1].response, second_response) == 0,
+ "Second round selected auth type wrong: expected %s, got %s\n",
+ second_response, sad.round[1].response);
}
- if (msg->status_code != final_status) {
- debug_printf (1, " Final status wrong: expected %u, got %u\n",
- final_status, msg->status_code);
- errors++;
- }
+ soup_test_assert_message_status (msg, final_status);
g_object_unref (msg);
soup_test_session_abort_unref (session);
@@ -873,7 +856,7 @@ do_select_auth_test (void)
SoupAuthDomain *basic_auth_domain, *digest_auth_domain;
SoupURI *uri;
- debug_printf (1, "\nTesting selection among multiple auths:\n");
+ g_test_bug ("562339");
/* It doesn't seem to be possible to configure Apache to serve
* multiple auth types for a single URL. So we have to use
@@ -1029,8 +1012,6 @@ do_auth_close_test (void)
SoupURI *uri;
AuthCloseData acd;
- debug_printf (1, "\nTesting auth when server times out connection:\n");
-
server = soup_test_server_new (FALSE);
soup_server_add_handler (server, NULL,
server_callback, NULL, NULL);
@@ -1057,18 +1038,130 @@ do_auth_close_test (void)
soup_uri_free (uri);
soup_session_send_message (acd.session, acd.msg);
- if (acd.msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Final status wrong: expected %u, got %u %s\n",
- SOUP_STATUS_OK, acd.msg->status_code,
- acd.msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (acd.msg, SOUP_STATUS_OK);
g_object_unref (acd.msg);
soup_test_session_abort_unref (acd.session);
soup_test_server_quit_unref (server);
}
+static gboolean
+infinite_cancel (gpointer session)
+{
+ soup_session_abort (session);
+ return FALSE;
+}
+
+static void
+infinite_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ soup_auth_authenticate (auth, "user", "bad");
+}
+
+static void
+do_infinite_auth_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ char *uri;
+ int timeout;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (infinite_authenticate), NULL);
+
+ uri = g_strconcat (base_uri, "Basic/realm1/", NULL);
+ msg = soup_message_new ("GET", uri);
+ g_free (uri);
+
+ timeout = g_timeout_add (500, infinite_cancel, session);
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*stuck in infinite loop*");
+ soup_session_send_message (session, msg);
+ g_test_assert_expected_messages ();
+
+ soup_test_assert (msg->status_code != SOUP_STATUS_CANCELLED,
+ "Got stuck in loop");
+ soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED);
+
+ g_source_remove (timeout);
+ soup_test_session_abort_unref (session);
+ g_object_unref (msg);
+}
+
+static void
+disappear_request_read (SoupServer *server, SoupMessage *msg,
+ SoupClientContext *context, gpointer user_data)
+{
+ /* Remove the WWW-Authenticate header if this was a failed attempt */
+ if (soup_message_headers_get_one (msg->request_headers, "Authorization") &&
+ msg->status_code == SOUP_STATUS_UNAUTHORIZED)
+ soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
+}
+
+static void
+disappear_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ int *counter = data;
+
+ (*counter)++;
+ if (!retrying)
+ soup_auth_authenticate (auth, "user", "bad");
+}
+
+static void
+do_disappearing_auth_test (void)
+{
+ SoupServer *server;
+ SoupAuthDomain *auth_domain;
+ SoupURI *uri;
+ SoupMessage *msg;
+ SoupSession *session;
+ int counter;
+
+ g_test_bug ("https://bugzilla.redhat.com/show_bug.cgi?id=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));
+
+ auth_domain = soup_auth_domain_basic_new (
+ SOUP_AUTH_DOMAIN_REALM, "auth-test",
+ SOUP_AUTH_DOMAIN_ADD_PATH, "/",
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, server_basic_auth_callback,
+ NULL);
+ soup_server_add_auth_domain (server, auth_domain);
+ g_signal_connect (server, "request-read",
+ G_CALLBACK (disappear_request_read), NULL);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ counter = 0;
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (disappear_authenticate), &counter);
+
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_session_send_message (session, msg);
+
+ soup_test_assert (counter <= 2,
+ "Got stuck in loop");
+ soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED);
+
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+
+ g_object_unref (auth_domain);
+ soup_uri_free (uri);
+ soup_test_server_quit_unref (server);
+}
+
static SoupAuthTest relogin_tests[] = {
{ "Auth provided via URL, should succeed",
"Basic/realm12/", "1", TRUE, "01", SOUP_STATUS_OK },
@@ -1090,25 +1183,28 @@ static SoupAuthTest relogin_tests[] = {
{ "Should fail with no auth, fail again with bad password, and give up",
"Basic/realm12/", "3", FALSE, "03", SOUP_STATUS_UNAUTHORIZED },
+
+ { NULL }
};
static void
-do_batch_tests (const gchar *base_uri_str, gint ntests)
+do_batch_tests (gconstpointer data)
{
+ const SoupAuthTest *current_tests = data;
SoupSession *session;
SoupMessage *msg;
char *expected, *uristr;
- SoupURI *base_uri;
+ SoupURI *base;
+ guint signal;
int i;
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- g_signal_connect (session, "authenticate",
- G_CALLBACK (authenticate), &i);
+ SOUP_TEST_SKIP_IF_NO_APACHE;
- base_uri = soup_uri_new (base_uri_str);
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ base = soup_uri_new (base_uri);
- for (i = 0; i < ntests; i++) {
- SoupURI *soup_uri = soup_uri_new_with_base (base_uri, current_tests[i].url);
+ for (i = 0; current_tests[i].url; i++) {
+ SoupURI *soup_uri = soup_uri_new_with_base (base, current_tests[i].url);
debug_printf (1, "Test %d: %s\n", i + 1, current_tests[i].explanation);
@@ -1139,30 +1235,24 @@ do_batch_tests (const gchar *base_uri_str, gint ntests)
soup_message_add_status_code_handler (
msg, "got_headers", SOUP_STATUS_OK,
G_CALLBACK (handler), expected);
+
+ signal = g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate),
+ (gpointer)&current_tests[i]);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_UNAUTHORIZED &&
- msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " %d %s !\n", msg->status_code,
- msg->reason_phrase);
- errors++;
- }
- if (*expected) {
- debug_printf (1, " expected %d more round(s)\n",
- (int)strlen (expected));
- errors++;
- }
- g_free (expected);
+ g_signal_handler_disconnect (session, signal);
- if (msg->status_code != current_tests[i].final_status) {
- debug_printf (1, " expected %d\n",
- current_tests[i].final_status);
- }
+ soup_test_assert_message_status (msg, current_tests[i].final_status);
+ soup_test_assert (!*expected,
+ "expected %d more round(s)\n",
+ (int)strlen (expected));
+ g_free (expected);
debug_printf (1, "\n");
g_object_unref (msg);
}
- soup_uri_free (base_uri);
+ soup_uri_free (base);
soup_test_session_abort_unref (session);
}
@@ -1170,31 +1260,27 @@ do_batch_tests (const gchar *base_uri_str, gint ntests)
int
main (int argc, char **argv)
{
- const char *base_uri;
- int ntests;
+ int ret;
test_init (argc, argv, NULL);
apache_init ();
base_uri = "http://127.0.0.1:47524/";
- /* Main tests */
- current_tests = main_tests;
- ntests = G_N_ELEMENTS (main_tests);
- do_batch_tests (base_uri, ntests);
-
- /* Re-login tests */
- current_tests = relogin_tests;
- ntests = G_N_ELEMENTS (relogin_tests);
- do_batch_tests (base_uri, ntests);
+ 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_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);
+ g_test_add_func ("/auth/async-auth/bad-password", do_async_auth_bad_password_test);
+ g_test_add_func ("/auth/async-auth/no-password", do_async_auth_no_password_test);
+ g_test_add_func ("/auth/select-auth", do_select_auth_test);
+ 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);
- /* Other regression tests */
- do_pipelined_auth_test (base_uri);
- do_digest_expiration_test (base_uri);
- do_async_auth_test (base_uri);
- do_select_auth_test ();
- do_auth_close_test ();
+ ret = g_test_run ();
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/cache-test.c b/tests/cache-test.c
new file mode 100644
index 00000000..3478f377
--- /dev/null
+++ b/tests/cache-test.c
@@ -0,0 +1,659 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#include "test-utils.h"
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ const char *last_modified, *etag;
+ const char *header;
+ guint status = SOUP_STATUS_OK;
+
+ if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-Expires");
+ if (header) {
+ soup_message_headers_append (msg->response_headers,
+ "Expires",
+ header);
+ }
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-Cache-Control");
+ if (header) {
+ soup_message_headers_append (msg->response_headers,
+ "Cache-Control",
+ header);
+ }
+
+ last_modified = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-Last-Modified");
+ if (last_modified) {
+ soup_message_headers_append (msg->response_headers,
+ "Last-Modified",
+ last_modified);
+ }
+
+ etag = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-ETag");
+ if (etag) {
+ soup_message_headers_append (msg->response_headers,
+ "ETag",
+ etag);
+ }
+
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "If-Modified-Since");
+ if (header && last_modified) {
+ SoupDate *date;
+ time_t lastmod, check;
+
+ date = soup_date_new_from_string (last_modified);
+ lastmod = soup_date_to_time_t (date);
+ soup_date_free (date);
+
+ date = soup_date_new_from_string (header);
+ check = soup_date_to_time_t (date);
+ soup_date_free (date);
+
+ if (lastmod <= check)
+ status = SOUP_STATUS_NOT_MODIFIED;
+ }
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "If-None-Match");
+ if (header && etag) {
+ if (!strcmp (header, etag))
+ status = SOUP_STATUS_NOT_MODIFIED;
+ }
+
+ header = soup_message_headers_get_one (msg->request_headers,
+ "Test-Set-My-Header");
+ if (header) {
+ soup_message_headers_append (msg->response_headers,
+ "My-Header",
+ header);
+ }
+
+ if (status == SOUP_STATUS_OK) {
+ GChecksum *sum;
+ const char *body;
+
+ sum = g_checksum_new (G_CHECKSUM_SHA256);
+ g_checksum_update (sum, (guchar *)path, strlen (path));
+ if (last_modified)
+ g_checksum_update (sum, (guchar *)last_modified, strlen (last_modified));
+ if (etag)
+ g_checksum_update (sum, (guchar *)etag, strlen (etag));
+ body = g_checksum_get_string (sum);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_COPY,
+ body, strlen (body) + 1);
+ g_checksum_free (sum);
+ }
+ soup_message_set_status (msg, status);
+}
+
+static gboolean
+is_network_stream (GInputStream *stream)
+{
+ while (G_IS_FILTER_INPUT_STREAM (stream))
+ stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+
+ return !G_IS_FILE_INPUT_STREAM (stream);
+}
+
+static char *do_request (SoupSession *session,
+ SoupURI *base_uri,
+ const char *method,
+ const char *path,
+ SoupMessageHeaders *response_headers,
+ ...) G_GNUC_NULL_TERMINATED;
+
+static gboolean last_request_hit_network;
+static gboolean last_request_validated;
+static guint cancelled_requests;
+
+static void
+copy_headers (const char *name,
+ const char *value,
+ gpointer user_data)
+{
+ SoupMessageHeaders *headers = (SoupMessageHeaders *) user_data;
+ soup_message_headers_append (headers, name, value);
+}
+
+static char *
+do_request (SoupSession *session,
+ SoupURI *base_uri,
+ const char *method,
+ const char *path,
+ SoupMessageHeaders *response_headers,
+ ...)
+{
+ SoupRequestHTTP *req;
+ SoupMessage *msg;
+ GInputStream *stream;
+ SoupURI *uri;
+ va_list ap;
+ const char *header, *value;
+ char buf[256];
+ gsize nread;
+ GError *error = NULL;
+
+ last_request_validated = last_request_hit_network = FALSE;
+
+ uri = soup_uri_new_with_base (base_uri, path);
+ req = soup_session_request_http_uri (session, method, uri, NULL);
+ soup_uri_free (uri);
+ msg = soup_request_http_get_message (req);
+
+ va_start (ap, response_headers);
+ while ((header = va_arg (ap, const char *))) {
+ value = va_arg (ap, const char *);
+ soup_message_headers_append (msg->request_headers,
+ header, value);
+ }
+ va_end (ap);
+
+ stream = soup_test_request_send (SOUP_REQUEST (req), NULL, 0, &error);
+ if (!stream) {
+ debug_printf (1, " could not send request: %s\n",
+ error->message);
+ g_error_free (error);
+ g_object_unref (req);
+ g_object_unref (msg);
+ return NULL;
+ }
+
+ if (response_headers)
+ soup_message_headers_foreach (msg->response_headers, copy_headers, response_headers);
+
+ g_object_unref (msg);
+
+ last_request_hit_network = is_network_stream (stream);
+
+ g_input_stream_read_all (stream, buf, sizeof (buf), &nread,
+ NULL, &error);
+ if (error) {
+ debug_printf (1, " could not read response: %s\n",
+ error->message);
+ g_clear_error (&error);
+ }
+ soup_test_request_close_stream (SOUP_REQUEST (req), stream,
+ NULL, &error);
+ if (error) {
+ debug_printf (1, " could not close stream: %s\n",
+ error->message);
+ g_clear_error (&error);
+ }
+ g_object_unref (stream);
+ g_object_unref (req);
+
+ /* Cache writes are G_PRIORITY_LOW, so they won't have happened yet... */
+ soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE));
+
+ return nread ? g_memdup (buf, nread) : g_strdup ("");
+}
+
+static void
+do_request_with_cancel (SoupSession *session,
+ SoupURI *base_uri,
+ const char *method,
+ const char *path,
+ SoupTestRequestFlags flags)
+{
+ SoupRequestHTTP *req;
+ GInputStream *stream;
+ SoupURI *uri;
+ GError *error = NULL;
+ GCancellable *cancellable;
+
+ last_request_validated = last_request_hit_network = FALSE;
+ cancelled_requests = 0;
+
+ uri = soup_uri_new_with_base (base_uri, path);
+ req = soup_session_request_http_uri (session, method, uri, NULL);
+ soup_uri_free (uri);
+ cancellable = flags & SOUP_TEST_REQUEST_CANCEL_CANCELLABLE ? g_cancellable_new () : NULL;
+ stream = soup_test_request_send (SOUP_REQUEST (req), cancellable, flags, &error);
+ if (stream) {
+ debug_printf (1, " could not cancel the request\n");
+ g_object_unref (stream);
+ g_object_unref (req);
+ return;
+ }
+
+ g_clear_object (&cancellable);
+ g_clear_object (&stream);
+ g_clear_object (&req);
+
+ soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE));
+}
+
+static void
+request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket)
+{
+ if (soup_message_headers_get_one (msg->request_headers,
+ "If-Modified-Since") ||
+ soup_message_headers_get_one (msg->request_headers,
+ "If-None-Match")) {
+ debug_printf (2, " Conditional request for %s\n",
+ soup_message_get_uri (msg)->path);
+ last_request_validated = TRUE;
+ }
+}
+
+static void
+request_unqueued (SoupSession *session, SoupMessage *msg,
+ gpointer data)
+{
+ if (msg->status_code == SOUP_STATUS_CANCELLED)
+ cancelled_requests++;
+}
+
+static void
+do_basics_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+ SoupCache *cache;
+ char *cache_dir;
+ char *body1, *body2, *body3, *body4, *body5, *cmp;
+
+ 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);
+ g_signal_connect (session, "request-started",
+ G_CALLBACK (request_started), NULL);
+
+ debug_printf (2, " Initial requests\n");
+ body1 = do_request (session, base_uri, "GET", "/1", NULL,
+ "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
+ NULL);
+ body2 = do_request (session, base_uri, "GET", "/2", NULL,
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
+ NULL);
+ body3 = do_request (session, base_uri, "GET", "/3", NULL,
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ body4 = do_request (session, base_uri, "GET", "/4", NULL,
+ "Test-Set-ETag", "\"abcdefg\"",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ body5 = do_request (session, base_uri, "GET", "/5", NULL,
+ "Test-Set-Cache-Control", "no-cache",
+ NULL);
+
+
+ /* Resource with future Expires should have been cached */
+ debug_printf (1, " Fresh cached resource\n");
+ cmp = do_request (session, base_uri, "GET", "/1", NULL,
+ NULL);
+ soup_test_assert (!last_request_hit_network,
+ "Request for /1 not filled from cache");
+ g_assert_cmpstr (body1, ==, cmp);
+ g_free (cmp);
+
+
+ /* Resource with long-ago Last-Modified should have been cached */
+ debug_printf (1, " Heuristically-fresh cached resource\n");
+ cmp = do_request (session, base_uri, "GET", "/2", NULL,
+ NULL);
+ soup_test_assert (!last_request_hit_network,
+ "Request for /2 not filled from cache");
+ g_assert_cmpstr (body2, ==, cmp);
+ g_free (cmp);
+
+
+ /* Adding a query string should bypass the cache but not invalidate it */
+ debug_printf (1, " Fresh cached resource with a query\n");
+ cmp = do_request (session, base_uri, "GET", "/1?attr=value", NULL,
+ NULL);
+ soup_test_assert (last_request_hit_network,
+ "Request for /1?attr=value filled from cache");
+ 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");
+ g_assert_cmpstr (body1, ==, cmp);
+ g_free (cmp);
+
+
+ /* Last-Modified + 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-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");
+ g_assert_cmpstr (body3, ==, cmp);
+ g_free (cmp);
+
+
+ /* Validation failure should update cache */
+ 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-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");
+ g_assert_cmpstr (body3, !=, cmp);
+ g_free (cmp);
+
+ debug_printf (2, " Second request\n");
+ cmp = do_request (session, base_uri, "GET", "/3", NULL,
+ "Test-Set-Last-Modified", "Sat, 02 Jan 2010 00:00:00 GMT",
+ "Test-Set-Cache-Control", "must-revalidate",
+ NULL);
+ soup_test_assert (last_request_validated,
+ "Second request for /3 not validated");
+ soup_test_assert (!last_request_hit_network,
+ "Second request for /3 not filled from cache");
+ g_assert_cmpstr (body3, !=, cmp);
+ g_free (cmp);
+
+ /* ETag + must-revalidate causes a conditional request */
+ debug_printf (1, " Unchanged must-revalidate resource w/ ETag\n");
+ cmp = do_request (session, base_uri, "GET", "/4", NULL,
+ "Test-Set-ETag", "\"abcdefg\"",
+ NULL);
+ soup_test_assert (last_request_validated,
+ "Request for /4 not validated");
+ soup_test_assert (!last_request_hit_network,
+ "Request for /4 not filled from cache");
+ g_assert_cmpstr (body4, ==, cmp);
+ g_free (cmp);
+
+
+ /* Cache-Control: no-cache prevents caching */
+ debug_printf (1, " Uncacheable resource\n");
+ cmp = do_request (session, base_uri, "GET", "/5", NULL,
+ "Test-Set-Cache-Control", "no-cache",
+ NULL);
+ soup_test_assert (last_request_hit_network,
+ "Request for /5 filled from cache");
+ g_assert_cmpstr (body5, ==, cmp);
+ g_free (cmp);
+
+
+ /* PUT to a URI invalidates the cache entry */
+ debug_printf (1, " Invalidating and re-requesting a cached resource\n");
+ cmp = do_request (session, base_uri, "PUT", "/1", NULL,
+ NULL);
+ soup_test_assert (last_request_hit_network,
+ "PUT filled from cache");
+ g_free (cmp);
+ cmp = do_request (session, base_uri, "GET", "/1", NULL,
+ NULL);
+ soup_test_assert (last_request_hit_network,
+ "PUT failed to invalidate cache entry");
+ g_assert_true (last_request_hit_network);
+ g_free (cmp);
+
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (cache);
+
+ g_free (cache_dir);
+ g_free (body1);
+ g_free (body2);
+ g_free (body3);
+ g_free (body4);
+ g_free (body5);
+}
+
+static void
+do_cancel_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+ SoupCache *cache;
+ char *cache_dir;
+ char *body1, *body2;
+ guint flags;
+
+ g_test_bug ("692310");
+
+ 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);
+ 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,
+ "Test-Set-Expires", "Fri, 01 Jan 2100 00:00:00 GMT",
+ 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);
+
+ /* Check that messages are correctly processed on cancellations. */
+ debug_printf (1, " Cancel fresh resource with soup_session_message_cancel()\n");
+ 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);
+
+ 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_session_abort_unref (session);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ SOUP_SESSION_ADD_FEATURE, cache,
+ NULL);
+ g_signal_connect (session, "request-unqueued",
+ G_CALLBACK (request_unqueued), NULL);
+
+ /* Check that messages are correctly processed on cancellations. */
+ debug_printf (1, " Cancel a revalidating resource with soup_session_message_cancel()\n");
+ 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);
+
+ 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_session_abort_unref (session);
+
+ g_object_unref (cache);
+ g_free (cache_dir);
+ g_free (body1);
+ g_free (body2);
+}
+
+static gboolean
+unref_stream (gpointer stream)
+{
+ g_object_unref (stream);
+ return FALSE;
+}
+
+static void
+base_stream_unreffed (gpointer loop, GObject *ex_base_stream)
+{
+ g_main_loop_quit (loop);
+}
+
+static void
+do_refcounting_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+ SoupCache *cache;
+ char *cache_dir;
+ SoupRequestHTTP *req;
+ GInputStream *stream, *base_stream;
+ SoupURI *uri;
+ GError *error = NULL;
+ guint flags;
+ GMainLoop *loop;
+
+ g_test_bug ("682527");
+
+ 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);
+
+ last_request_validated = last_request_hit_network = FALSE;
+ cancelled_requests = 0;
+
+ uri = soup_uri_new_with_base (base_uri, "/1");
+ req = soup_session_request_http_uri (session, "GET", uri, NULL);
+ soup_uri_free (uri);
+
+ flags = SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH | SOUP_TEST_REQUEST_CANCEL_MESSAGE;
+ stream = soup_test_request_send (SOUP_REQUEST (req), NULL, flags, &error);
+ if (!stream) {
+ debug_printf (1, " could not send request: %s\n",
+ error->message);
+ g_error_free (error);
+ g_object_unref (req);
+ return;
+ }
+ g_object_unref (req);
+
+ base_stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (stream));
+
+ debug_printf (1, " Checking that the base stream is properly unref'ed\n");
+ loop = g_main_loop_new (NULL, FALSE);
+ g_object_weak_ref (G_OBJECT (base_stream), base_stream_unreffed, loop);
+ g_idle_add (unref_stream, stream);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ soup_cache_flush ((SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE));
+
+ soup_test_session_abort_unref (session);
+
+ g_object_unref (cache);
+ g_free (cache_dir);
+}
+
+static void
+do_headers_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+ SoupMessageHeaders *headers;
+ SoupCache *cache;
+ char *cache_dir;
+ char *body1, *cmp;
+ const char *header_value;
+
+ 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);
+
+ g_signal_connect (session, "request-started",
+ G_CALLBACK (request_started), NULL);
+
+ debug_printf (2, " Initial requests\n");
+ body1 = do_request (session, base_uri, "GET", "/1", NULL,
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2100 00:00:00 GMT",
+ "Test-Set-My-Header", "My header value",
+ NULL);
+
+ /* My-Header new value should be updated in cache */
+ debug_printf (2, " Fresh cached resource which updates My-Header\n");
+ cmp = do_request (session, base_uri, "GET", "/1", NULL,
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
+ "Test-Set-My-Header", "My header NEW value",
+ NULL);
+ soup_test_assert (last_request_validated,
+ "Request for /1 not validated");
+ soup_test_assert (!last_request_hit_network,
+ "Request for /1 not filled from cache");
+ g_free (cmp);
+
+ /* Check that cache returns the updated header */
+ debug_printf (2, " Fresh cached resource with new value for My-Header\n");
+ headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+ cmp = do_request (session, base_uri, "GET", "/1", headers,
+ "Test-Set-Last-Modified", "Fri, 01 Jan 2010 00:00:00 GMT",
+ NULL);
+ soup_test_assert (!last_request_hit_network,
+ "Request for /1 not filled from cache");
+ g_free (cmp);
+
+ header_value = soup_message_headers_get_list (headers, "My-Header");
+ g_assert_cmpstr (header_value, ==, "My header NEW value");
+ soup_message_headers_free (headers);
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (cache);
+
+ g_free (cache_dir);
+ g_free (body1);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ SoupURI *base_uri;
+ int ret;
+
+ test_init (argc, argv, NULL);
+
+ 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));
+
+ 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);
+
+ ret = g_test_run ();
+
+ soup_uri_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/chunk-io-test.c b/tests/chunk-io-test.c
new file mode 100644
index 00000000..1e53eef1
--- /dev/null
+++ b/tests/chunk-io-test.c
@@ -0,0 +1,610 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#include "test-utils.h"
+
+static void
+force_io_streams_init (void)
+{
+ SoupServer *server;
+ SoupSession *session;
+ guint port;
+ SoupURI *base_uri;
+ SoupMessage *msg;
+
+ /* Poke libsoup enough to cause SoupBodyInputStream and
+ * SoupBodyOutputStream to get defined, so we can find them
+ * via g_type_from_name() later.
+ */
+
+ 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);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+ msg = soup_message_new_from_uri ("POST", base_uri);
+ soup_session_send_message (session, msg);
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
+
+ soup_uri_free (base_uri);
+ soup_test_server_quit_unref (server);
+}
+
+typedef struct {
+ GFilterInputStream grandparent;
+
+ gpointer *soup_filter_input_stream_private;
+
+ gboolean is_readable;
+} SlowInputStream;
+
+typedef struct {
+ GFilterInputStreamClass grandparent;
+} SlowInputStreamClass;
+
+GType slow_input_stream_get_type (void);
+static void slow_pollable_input_stream_init (GPollableInputStreamInterface *pollable_interface,
+ gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (SlowInputStream, slow_input_stream,
+ g_type_from_name ("SoupFilterInputStream"),
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, slow_pollable_input_stream_init);
+ )
+
+static void
+slow_input_stream_init (SlowInputStream *sis)
+{
+}
+
+static gssize
+slow_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_input_stream_read (G_FILTER_INPUT_STREAM (stream)->base_stream,
+ buffer, 1, cancellable, error);
+}
+
+static void
+slow_input_stream_class_init (SlowInputStreamClass *sisclass)
+{
+ GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (sisclass);
+
+ input_stream_class->read_fn = slow_input_stream_read;
+}
+
+static gboolean
+slow_input_stream_is_readable (GPollableInputStream *stream)
+{
+ return ((SlowInputStream *)stream)->is_readable;
+}
+
+static gssize
+slow_input_stream_read_nonblocking (GPollableInputStream *stream,
+ void *buffer,
+ gsize count,
+ GError **error)
+{
+ if (((SlowInputStream *)stream)->is_readable) {
+ ((SlowInputStream *)stream)->is_readable = FALSE;
+ return slow_input_stream_read (G_INPUT_STREAM (stream), buffer, count,
+ NULL, error);
+ } else {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+ "would block");
+ return -1;
+ }
+}
+
+static GSource *
+slow_input_stream_create_source (GPollableInputStream *stream,
+ GCancellable *cancellable)
+{
+ GSource *base_source, *pollable_source;
+
+ ((SlowInputStream *)stream)->is_readable = TRUE;
+ base_source = g_timeout_source_new (0);
+ g_source_set_dummy_callback (base_source);
+
+ pollable_source = g_pollable_source_new (G_OBJECT (stream));
+ g_source_add_child_source (pollable_source, base_source);
+ g_source_unref (base_source);
+
+ return pollable_source;
+}
+
+static void
+slow_pollable_input_stream_init (GPollableInputStreamInterface *pollable_interface,
+ gpointer interface_data)
+{
+ pollable_interface->is_readable = slow_input_stream_is_readable;
+ pollable_interface->read_nonblocking = slow_input_stream_read_nonblocking;
+ pollable_interface->create_source = slow_input_stream_create_source;
+}
+
+typedef struct {
+ GFilterOutputStream parent;
+
+ gboolean is_writable;
+} SlowOutputStream;
+
+typedef struct {
+ GFilterOutputStreamClass parent;
+} SlowOutputStreamClass;
+
+GType slow_output_stream_get_type (void);
+
+static void slow_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface,
+ gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (SlowOutputStream, slow_output_stream,
+ g_type_from_name ("GFilterOutputStream"),
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, slow_pollable_output_stream_init);
+ )
+
+static void
+slow_output_stream_init (SlowOutputStream *sis)
+{
+}
+
+static gssize
+slow_output_stream_write (GOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_output_stream_write (G_FILTER_OUTPUT_STREAM (stream)->base_stream,
+ buffer, 1, cancellable, error);
+}
+
+static void
+slow_output_stream_class_init (SlowOutputStreamClass *sisclass)
+{
+ GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (sisclass);
+
+ output_stream_class->write_fn = slow_output_stream_write;
+}
+
+static gboolean
+slow_output_stream_is_writable (GPollableOutputStream *stream)
+{
+ return ((SlowOutputStream *)stream)->is_writable;
+}
+
+static gssize
+slow_output_stream_write_nonblocking (GPollableOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GError **error)
+{
+ if (((SlowOutputStream *)stream)->is_writable) {
+ ((SlowOutputStream *)stream)->is_writable = FALSE;
+ return slow_output_stream_write (G_OUTPUT_STREAM (stream), buffer, count,
+ NULL, error);
+ } else {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+ "would block");
+ return -1;
+ }
+}
+
+static GSource *
+slow_output_stream_create_source (GPollableOutputStream *stream,
+ GCancellable *cancellable)
+{
+ GSource *base_source, *pollable_source;
+
+ ((SlowOutputStream *)stream)->is_writable = TRUE;
+ base_source = g_timeout_source_new (0);
+ g_source_set_dummy_callback (base_source);
+
+ pollable_source = g_pollable_source_new (G_OBJECT (stream));
+ g_source_add_child_source (pollable_source, base_source);
+ g_source_unref (base_source);
+
+ return pollable_source;
+}
+
+static void
+slow_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface,
+ gpointer interface_data)
+{
+ pollable_interface->is_writable = slow_output_stream_is_writable;
+ pollable_interface->write_nonblocking = slow_output_stream_write_nonblocking;
+ pollable_interface->create_source = slow_output_stream_create_source;
+}
+
+typedef struct {
+ GFilterOutputStream parent;
+
+ gboolean is_broken;
+} BreakingOutputStream;
+
+typedef struct {
+ GFilterOutputStreamClass parent;
+} BreakingOutputStreamClass;
+
+GType breaking_output_stream_get_type (void);
+
+static void breaking_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface,
+ gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (BreakingOutputStream, breaking_output_stream,
+ g_type_from_name ("GFilterOutputStream"),
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, breaking_pollable_output_stream_init);
+ )
+
+static void
+breaking_output_stream_init (BreakingOutputStream *sis)
+{
+}
+
+static gssize
+breaking_output_stream_write (GOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (((BreakingOutputStream *)stream)->is_broken) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "failed");
+ return -1;
+ }
+
+ if (count > 128) {
+ ((BreakingOutputStream *)stream)->is_broken = TRUE;
+ count /= 2;
+ }
+ return g_output_stream_write (G_FILTER_OUTPUT_STREAM (stream)->base_stream,
+ buffer, count, cancellable, error);
+}
+
+static void
+breaking_output_stream_class_init (BreakingOutputStreamClass *sisclass)
+{
+ GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (sisclass);
+
+ output_stream_class->write_fn = breaking_output_stream_write;
+}
+
+static gboolean
+breaking_output_stream_is_writable (GPollableOutputStream *stream)
+{
+ return TRUE;
+}
+
+static gssize
+breaking_output_stream_write_nonblocking (GPollableOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GError **error)
+{
+ if (((BreakingOutputStream *)stream)->is_broken) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "failed");
+ return -1;
+ }
+
+ if (count > 128) {
+ ((BreakingOutputStream *)stream)->is_broken = TRUE;
+ count /= 2;
+ }
+ return g_pollable_output_stream_write_nonblocking (G_POLLABLE_OUTPUT_STREAM (G_FILTER_OUTPUT_STREAM (stream)->base_stream),
+ buffer, count, NULL, error);
+}
+
+static GSource *
+breaking_output_stream_create_source (GPollableOutputStream *stream,
+ GCancellable *cancellable)
+{
+ GSource *base_source, *pollable_source;
+
+ base_source = g_timeout_source_new (0);
+ g_source_set_dummy_callback (base_source);
+
+ pollable_source = g_pollable_source_new (G_OBJECT (stream));
+ g_source_add_child_source (pollable_source, base_source);
+ g_source_unref (base_source);
+
+ return pollable_source;
+}
+
+static void
+breaking_pollable_output_stream_init (GPollableOutputStreamInterface *pollable_interface,
+ gpointer interface_data)
+{
+ pollable_interface->is_writable = breaking_output_stream_is_writable;
+ pollable_interface->write_nonblocking = breaking_output_stream_write_nonblocking;
+ pollable_interface->create_source = breaking_output_stream_create_source;
+}
+
+#define CHUNK_SIZE 1024
+
+static GString *
+chunkify (const char *str, gsize length)
+{
+ GString *gstr;
+ int i, size;
+
+ gstr = g_string_new (NULL);
+ for (i = 0; i < length; i += CHUNK_SIZE) {
+ size = MIN (CHUNK_SIZE, length - i);
+ g_string_append_printf (gstr, "%x\r\n", size);
+ g_string_append_len (gstr, str + i, size);
+ g_string_append (gstr, "\r\n");
+ }
+ g_string_append (gstr, "0\r\n\r\n");
+
+ return gstr;
+}
+
+static void
+do_io_tests (void)
+{
+ GInputStream *imem, *islow, *in;
+ GOutputStream *omem, *oslow, *out;
+ GMemoryOutputStream *mem;
+ SoupBuffer *raw_contents;
+ char *buf;
+ GString *chunkified;
+ GError *error = NULL;
+ gssize nread, nwrote, total;
+ gssize chunk_length, chunk_total;
+
+ raw_contents = soup_test_get_index ();
+ chunkified = chunkify (raw_contents->data, raw_contents->length);
+
+ debug_printf (1, " sync read\n");
+
+ imem = g_memory_input_stream_new_from_data (chunkified->str, chunkified->len, NULL);
+ islow = g_object_new (slow_input_stream_get_type (),
+ "base-stream", imem,
+ "close-base-stream", TRUE,
+ NULL);
+ in = g_object_new (g_type_from_name ("SoupBodyInputStream"),
+ "base-stream", islow,
+ "close-base-stream", TRUE,
+ "encoding", SOUP_ENCODING_CHUNKED,
+ NULL);
+ g_object_unref (imem);
+ g_object_unref (islow);
+
+ buf = g_malloc (raw_contents->length);
+ total = 0;
+ while (TRUE) {
+ nread = g_input_stream_read (in, buf + total,
+ raw_contents->length - total,
+ NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ if (nread > 0)
+ total += nread;
+ else
+ break;
+ }
+
+ g_input_stream_close (in, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ g_object_unref (in);
+
+ soup_assert_cmpmem (buf, total, raw_contents->data, raw_contents->length);
+ g_free (buf);
+
+ debug_printf (1, " async read\n");
+
+ imem = g_memory_input_stream_new_from_data (chunkified->str, chunkified->len, NULL);
+ islow = g_object_new (slow_input_stream_get_type (),
+ "base-stream", imem,
+ "close-base-stream", TRUE,
+ NULL);
+ in = g_object_new (g_type_from_name ("SoupBodyInputStream"),
+ "base-stream", islow,
+ "close-base-stream", TRUE,
+ "encoding", SOUP_ENCODING_CHUNKED,
+ NULL);
+ g_object_unref (imem);
+ g_object_unref (islow);
+
+ buf = g_malloc (raw_contents->length);
+ total = 0;
+ while (TRUE) {
+ nread = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in),
+ buf + total,
+ raw_contents->length - total,
+ NULL, &error);
+ if (nread == -1 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ GSource *source;
+
+ g_clear_error (&error);
+ source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in), NULL);
+ g_source_set_dummy_callback (source);
+ g_source_attach (source, NULL);
+ while (!g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (in)))
+ g_main_context_iteration (NULL, TRUE);
+ g_source_destroy (source);
+ g_source_unref (source);
+ continue;
+ } else if (nread == -1) {
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ break;
+ } else if (nread == 0)
+ break;
+ else
+ total += nread;
+ }
+
+ g_input_stream_close (in, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ g_object_unref (in);
+
+ soup_assert_cmpmem (buf, total, raw_contents->data, raw_contents->length);
+ g_free (buf);
+
+ debug_printf (1, " sync write\n");
+
+ buf = g_malloc (chunkified->len);
+ omem = g_memory_output_stream_new (buf, chunkified->len, NULL, NULL);
+ oslow = g_object_new (slow_output_stream_get_type (),
+ "base-stream", omem,
+ "close-base-stream", TRUE,
+ NULL);
+ out = g_object_new (g_type_from_name ("SoupBodyOutputStream"),
+ "base-stream", oslow,
+ "close-base-stream", TRUE,
+ "encoding", SOUP_ENCODING_CHUNKED,
+ NULL);
+ g_object_unref (omem);
+ g_object_unref (oslow);
+
+ total = chunk_length = chunk_total = 0;
+ while (total < raw_contents->length) {
+ if (chunk_total == chunk_length) {
+ chunk_length = MIN (CHUNK_SIZE, raw_contents->length - total);
+ chunk_total = 0;
+ }
+ nwrote = g_output_stream_write (out, raw_contents->data + total,
+ chunk_length - chunk_total, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ if (nwrote > 0) {
+ total += nwrote;
+ chunk_total += nwrote;
+ } else
+ break;
+ }
+
+ g_output_stream_close (out, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+
+ mem = G_MEMORY_OUTPUT_STREAM (omem);
+ soup_assert_cmpmem (g_memory_output_stream_get_data (mem),
+ g_memory_output_stream_get_data_size (mem),
+ chunkified->str, chunkified->len);
+
+ g_object_unref (out);
+ g_free (buf);
+
+ debug_printf (1, " async write\n");
+
+ buf = g_malloc (chunkified->len);
+ omem = g_memory_output_stream_new (buf, chunkified->len, NULL, NULL);
+ oslow = g_object_new (slow_output_stream_get_type (),
+ "base-stream", omem,
+ "close-base-stream", TRUE,
+ NULL);
+ out = g_object_new (g_type_from_name ("SoupBodyOutputStream"),
+ "base-stream", oslow,
+ "close-base-stream", TRUE,
+ "encoding", SOUP_ENCODING_CHUNKED,
+ NULL);
+ g_object_unref (omem);
+ g_object_unref (oslow);
+
+ total = chunk_length = chunk_total = 0;
+ while (total < raw_contents->length) {
+ if (chunk_total == chunk_length) {
+ chunk_length = MIN (CHUNK_SIZE, raw_contents->length - total);
+ chunk_total = 0;
+ }
+ nwrote = g_pollable_output_stream_write_nonblocking (G_POLLABLE_OUTPUT_STREAM (out),
+ raw_contents->data + total,
+ chunk_length - chunk_total,
+ NULL, &error);
+ if (nwrote == -1 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ GSource *source;
+
+ g_clear_error (&error);
+ source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (out), NULL);
+ g_source_set_dummy_callback (source);
+ g_source_attach (source, NULL);
+ while (!g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (out)))
+ g_main_context_iteration (NULL, TRUE);
+ g_source_destroy (source);
+ g_source_unref (source);
+ continue;
+ } else if (nwrote == -1) {
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ break;
+ } else {
+ total += nwrote;
+ chunk_total += nwrote;
+ }
+ }
+
+ g_output_stream_close (out, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+
+ mem = G_MEMORY_OUTPUT_STREAM (omem);
+ soup_assert_cmpmem (g_memory_output_stream_get_data (mem),
+ g_memory_output_stream_get_data_size (mem),
+ chunkified->str, chunkified->len);
+
+ g_object_unref (out);
+ g_free (buf);
+
+ debug_printf (1, " failed write\n");
+ /* this succeeds if it doesn't critical */
+
+ buf = g_malloc (chunkified->len);
+ omem = g_memory_output_stream_new (buf, chunkified->len, NULL, NULL);
+ oslow = g_object_new (breaking_output_stream_get_type (),
+ "base-stream", omem,
+ "close-base-stream", TRUE,
+ NULL);
+ out = g_object_new (g_type_from_name ("SoupBodyOutputStream"),
+ "base-stream", oslow,
+ "close-base-stream", TRUE,
+ "encoding", SOUP_ENCODING_CHUNKED,
+ NULL);
+ g_object_unref (omem);
+ g_object_unref (oslow);
+
+ total = 0;
+ while (total < raw_contents->length) {
+ nwrote = g_output_stream_write (out, raw_contents->data + total,
+ raw_contents->length - total, NULL, NULL);
+ if (nwrote == -1)
+ break;
+ else
+ total += nwrote;
+ }
+
+ g_assert_cmpint (total, !=, raw_contents->length);
+
+ g_output_stream_close (out, NULL, NULL);
+ g_object_unref (out);
+
+ g_free (buf);
+
+ g_string_free (chunkified, TRUE);
+}
+
+int
+main (int argc, char **argv)
+{
+ int ret;
+
+ test_init (argc, argv, NULL);
+
+ force_io_streams_init ();
+
+ g_test_add_func ("/chunk-io", do_io_tests);
+
+ ret = g_test_run ();
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/chunk-test.c b/tests/chunk-test.c
index cce7127f..864f8620 100644
--- a/tests/chunk-test.c
+++ b/tests/chunk-test.c
@@ -5,6 +5,9 @@
#include "test-utils.h"
+static SoupSession *session;
+static SoupURI *base_uri;
+
typedef struct {
SoupSession *session;
SoupBuffer *chunks[3];
@@ -19,9 +22,9 @@ write_next_chunk (SoupMessage *msg, gpointer user_data)
debug_printf (2, " writing chunk %d\n", ptd->next);
- if (ptd->streaming && ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
- debug_printf (1, " error: next chunk requested before last one freed!\n");
- errors++;
+ if (ptd->streaming && ptd->next > 0) {
+ soup_test_assert (ptd->chunks[ptd->next - 1] == NULL,
+ "next chunk requested before last one freed");
}
if (ptd->next < G_N_ELEMENTS (ptd->chunks)) {
@@ -50,8 +53,8 @@ write_next_chunk_streaming_hack (SoupMessage *msg, gpointer user_data)
soup_message_body_wrote_chunk (msg->request_body, chunk);
soup_buffer_free (chunk);
} else {
- debug_printf (1, " error: written chunk does not exist!\n");
- errors++;
+ soup_test_assert (chunk,
+ "written chunk does not exist");
}
write_next_chunk (msg, user_data);
}
@@ -77,8 +80,8 @@ clear_buffer_ptr (gpointer data)
g_free ((char *)(*buffer_ptr)->data);
*buffer_ptr = NULL;
} else {
- debug_printf (2, " chunk is already clear!\n");
- errors++;
+ soup_test_assert (*buffer_ptr,
+ "chunk is already clear");
}
}
@@ -136,26 +139,20 @@ typedef enum {
} RequestTestFlags;
static void
-do_request_test (SoupSession *session, SoupURI *base_uri, RequestTestFlags flags)
+do_request_test (gconstpointer data)
{
- SoupURI *uri = base_uri;
+ RequestTestFlags flags = GPOINTER_TO_UINT (data);
+ SoupURI *uri;
PutTestData ptd;
SoupMessage *msg;
const char *client_md5, *server_md5;
GChecksum *check;
int i, length;
- debug_printf (1, "PUT");
- if (flags & HACKY_STREAMING)
- debug_printf (1, " w/ hacky streaming");
- else if (flags & PROPER_STREAMING)
- debug_printf (1, " w/ proper streaming");
- if (flags & RESTART) {
- debug_printf (1, " and restart");
+ if (flags & RESTART)
+ uri = soup_uri_new_with_base (base_uri, "/redirect");
+ else
uri = soup_uri_copy (base_uri);
- soup_uri_set_path (uri, "/redirect");
- }
- debug_printf (1, "\n");
ptd.session = session;
setup_request_body (&ptd);
@@ -199,35 +196,19 @@ do_request_test (SoupSession *session, SoupURI *base_uri, RequestTestFlags flags
G_CALLBACK (wrote_body_data), &ptd);
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " message failed: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
-
- if (msg->request_body->data) {
- debug_printf (1, " msg->request_body set!\n");
- errors++;
- }
- if (msg->request_body->length != length || length != ptd.nwrote) {
- debug_printf (1, " sent length mismatch: %d vs %d vs %d\n",
- (int)msg->request_body->length, length, ptd.nwrote);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CREATED);
+ g_assert_null (msg->request_body->data);
+ g_assert_cmpint (msg->request_body->length, ==, length);
+ g_assert_cmpint (length, ==, ptd.nwrote);
server_md5 = soup_message_headers_get_one (msg->response_headers,
"Content-MD5");
- if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
- debug_printf (1, " client/server data mismatch: %s vs %s\n",
- client_md5, server_md5 ? server_md5 : "(null)");
- errors++;
- }
+ g_assert_cmpstr (client_md5, ==, server_md5);
g_object_unref (msg);
g_checksum_free (check);
- if (uri != base_uri)
- soup_uri_free (uri);
+ soup_uri_free (uri);
}
typedef struct {
@@ -243,10 +224,8 @@ chunk_allocator (SoupMessage *msg, gsize max_len, gpointer user_data)
debug_printf (2, " allocating chunk\n");
- if (gtd->current_chunk) {
- debug_printf (1, " error: next chunk allocated before last one freed!\n");
- errors++;
- }
+ soup_test_assert (gtd->current_chunk == NULL,
+ "error: next chunk allocated before last one freed");
gtd->current_chunk = soup_buffer_new_with_owner (g_malloc (6), 6,
&gtd->current_chunk,
clear_buffer_ptr);
@@ -270,56 +249,40 @@ got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
}
static void
-do_response_test (SoupSession *session, SoupURI *base_uri)
+do_response_test (void)
{
GetTestData gtd;
SoupMessage *msg;
const char *client_md5, *server_md5;
- debug_printf (1, "GET\n");
-
gtd.current_chunk = NULL;
gtd.length = 0;
gtd.check = g_checksum_new (G_CHECKSUM_MD5);
msg = soup_message_new_from_uri ("GET", base_uri);
soup_message_body_set_accumulate (msg->response_body, FALSE);
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
soup_message_set_chunk_allocator (msg, chunk_allocator, &gtd, NULL);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
g_signal_connect (msg, "got_chunk",
G_CALLBACK (got_chunk), &gtd);
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " message failed: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
-
- if (msg->response_body->data) {
- debug_printf (1, " msg->response_body set!\n");
- errors++;
- }
- if (soup_message_headers_get_content_length (msg->response_headers) != gtd.length) {
- debug_printf (1, " received length mismatch: %d vs %d\n",
- (int)soup_message_headers_get_content_length (msg->response_headers), gtd.length);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_assert_null (msg->response_body->data);
+ g_assert_cmpint (soup_message_headers_get_content_length (msg->response_headers), ==, gtd.length);
client_md5 = g_checksum_get_string (gtd.check);
server_md5 = soup_message_headers_get_one (msg->response_headers,
"Content-MD5");
- if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
- debug_printf (1, " client/server data mismatch: %s vs %s\n",
- client_md5, server_md5 ? server_md5 : "(null)");
- errors++;
- }
+ g_assert_cmpstr (client_md5, ==, server_md5);
g_object_unref (msg);
g_checksum_free (gtd.check);
}
/* Make sure TEMPORARY buffers are handled properly with non-accumulating
- * message bodies. Part of https://bugs.webkit.org/show_bug.cgi?id=18343
+ * message bodies.
*/
static void
@@ -334,24 +297,24 @@ temp_test_wrote_chunk (SoupMessage *msg, gpointer session)
* the I/O to stall since soup-message-io will think it's
* done, but it hasn't written Content-Length bytes yet.
*/
- if (!chunk) {
- debug_printf (1, " Lost second chunk!\n");
- errors++;
- soup_session_abort (session);
- } else
+ if (chunk)
soup_buffer_free (chunk);
+ else {
+ soup_test_assert (chunk, "Lost second chunk");
+ soup_session_abort (session);
+ }
g_signal_handlers_disconnect_by_func (msg, temp_test_wrote_chunk, session);
}
static void
-do_temporary_test (SoupSession *session, SoupURI *base_uri)
+do_temporary_test (void)
{
SoupMessage *msg;
char *client_md5;
const char *server_md5;
- debug_printf (1, "PUT w/ temporary buffers\n");
+ g_test_bug ("https://bugs.webkit.org/show_bug.cgi?id=18343");
msg = soup_message_new_from_uri ("PUT", base_uri);
soup_message_body_append (msg->request_body, SOUP_MEMORY_TEMPORARY,
@@ -366,19 +329,11 @@ do_temporary_test (SoupSession *session, SoupURI *base_uri)
G_CALLBACK (temp_test_wrote_chunk), session);
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " message failed: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CREATED);
server_md5 = soup_message_headers_get_one (msg->response_headers,
"Content-MD5");
- if (!server_md5 || strcmp (client_md5, server_md5) != 0) {
- debug_printf (1, " client/server data mismatch: %s vs %s\n",
- client_md5, server_md5 ? server_md5 : "(null)");
- errors++;
- }
+ g_assert_cmpstr (client_md5, ==, server_md5);
g_free (client_md5);
g_object_unref (msg);
@@ -396,24 +351,20 @@ large_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
{
LargeChunkData *lcd = user_data;
- if (memcmp (chunk->data, lcd->buf->data + lcd->offset, chunk->length) != 0) {
- debug_printf (1, " chunk data mismatch at %ld\n", (long)lcd->offset);
- errors++;
- } else
- debug_printf (2, " chunk data match at %ld\n", (long)lcd->offset);
+ soup_assert_cmpmem (chunk->data, chunk->length,
+ lcd->buf->data + lcd->offset,
+ chunk->length);
lcd->offset += chunk->length;
}
static void
-do_large_chunk_test (SoupSession *session, SoupURI *base_uri)
+do_large_chunk_test (void)
{
SoupMessage *msg;
char *buf_data;
int i;
LargeChunkData lcd;
- debug_printf (1, "PUT w/ large chunk\n");
-
msg = soup_message_new_from_uri ("PUT", base_uri);
buf_data = g_malloc0 (LARGE_CHUNK_SIZE);
@@ -428,41 +379,13 @@ do_large_chunk_test (SoupSession *session, SoupURI *base_uri)
G_CALLBACK (large_wrote_body_data), &lcd);
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " message failed: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CREATED);
soup_buffer_free (lcd.buf);
g_object_unref (msg);
}
static void
-do_chunk_tests (SoupURI *base_uri)
-{
- SoupSession *session;
-
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- do_request_test (session, base_uri, 0);
- debug_printf (2, "\n\n");
- do_request_test (session, base_uri, PROPER_STREAMING);
- debug_printf (2, "\n\n");
- do_request_test (session, base_uri, PROPER_STREAMING | RESTART);
- debug_printf (2, "\n\n");
- do_request_test (session, base_uri, HACKY_STREAMING);
- debug_printf (2, "\n\n");
- do_request_test (session, base_uri, HACKY_STREAMING | RESTART);
- debug_printf (2, "\n\n");
- do_response_test (session, base_uri);
- debug_printf (2, "\n\n");
- do_temporary_test (session, base_uri);
- debug_printf (2, "\n\n");
- do_large_chunk_test (session, base_uri);
- soup_test_session_abort_unref (session);
-}
-
-static void
server_callback (SoupServer *server, SoupMessage *msg,
const char *path, GHashTable *query,
SoupClientContext *context, gpointer data)
@@ -505,7 +428,7 @@ main (int argc, char **argv)
GMainLoop *loop;
SoupServer *server;
guint port;
- SoupURI *base_uri;
+ int ret;
test_init (argc, argv, NULL);
@@ -518,12 +441,27 @@ main (int argc, char **argv)
base_uri = soup_uri_new ("http://127.0.0.1");
soup_uri_set_port (base_uri, port);
- do_chunk_tests (base_uri);
+
+ 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);
+ g_test_add_data_func ("/chunks/request/proper-streaming", GINT_TO_POINTER (PROPER_STREAMING), do_request_test);
+ g_test_add_data_func ("/chunks/request/proper-streaming/restart", GINT_TO_POINTER (PROPER_STREAMING | RESTART), do_request_test);
+ g_test_add_data_func ("/chunks/request/hacky-streaming", GINT_TO_POINTER (HACKY_STREAMING), do_request_test);
+ g_test_add_data_func ("/chunks/request/hacky-streaming/restart", GINT_TO_POINTER (HACKY_STREAMING | RESTART), do_request_test);
+ g_test_add_func ("/chunks/response", do_response_test);
+ g_test_add_func ("/chunks/temporary", do_temporary_test);
+ g_test_add_func ("/chunks/large", do_large_chunk_test);
+
+ ret = g_test_run ();
+
+ soup_test_session_abort_unref (session);
+
soup_uri_free (base_uri);
g_main_loop_unref (loop);
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/coding-test.c b/tests/coding-test.c
index cda4b89e..0445f632 100644
--- a/tests/coding-test.c
+++ b/tests/coding-test.c
@@ -16,8 +16,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
{
const char *accept_encoding, *options;
GSList *codings;
- char *file = NULL, *contents;
- gsize length;
+ SoupBuffer *response = NULL;
options = soup_message_headers_get_one (msg->request_headers,
"X-Test-Options");
@@ -33,7 +32,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
if (codings) {
gboolean claim_deflate, claim_gzip;
- const char *file_path = NULL, *encoding = NULL;
+ const char *extension = NULL, *encoding = NULL;
claim_deflate = g_slist_find_custom (codings, "deflate", (GCompareFunc)g_ascii_strcasecmp) != NULL;
claim_gzip = g_slist_find_custom (codings, "gzip", (GCompareFunc)g_ascii_strcasecmp) != NULL;
@@ -41,44 +40,44 @@ server_callback (SoupServer *server, SoupMessage *msg,
if (claim_gzip && (!claim_deflate ||
(!soup_header_contains (options, "prefer-deflate-zlib") &&
!soup_header_contains (options, "prefer-deflate-raw")))) {
- file_path = SRCDIR "/resources%s.gz";
+ extension = "gz";
encoding = "gzip";
} else if (claim_deflate) {
if (soup_header_contains (options, "prefer-deflate-raw")) {
- file_path = SRCDIR "/resources%s.raw";
+ extension = "raw";
encoding = "deflate";
} else {
- file_path = SRCDIR "/resources%s.zlib";
+ extension = "zlib";
encoding = "deflate";
}
}
- if (file_path && encoding) {
- file = g_strdup_printf (file_path, path);
- if (g_file_test (file, G_FILE_TEST_EXISTS)) {
+ if (extension && encoding) {
+ char *resource;
+
+ resource = g_strdup_printf ("%s.%s", path, extension);
+ response = soup_test_load_resource (resource, NULL);
+
+ if (response) {
soup_message_headers_append (msg->response_headers,
"Content-Encoding",
encoding);
- } else {
- g_free (file);
- file = NULL;
}
+ g_free (resource);
}
}
soup_header_free_list (codings);
- if (!file)
- file = g_strdup_printf (SRCDIR "/resources%s", path);
- if (!g_file_get_contents (file, &contents, &length, NULL)) {
+ if (!response)
+ response = soup_test_load_resource (path, NULL);
+ if (!response) {
/* If path.gz exists but can't be read, we'll send back
* the error with "Content-Encoding: gzip" but there's
* no body, so, eh.
*/
- g_free (file);
soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
return;
}
- g_free (file);
if (soup_header_contains (options, "force-encode")) {
const gchar *encoding = "gzip";
@@ -106,10 +105,9 @@ server_callback (SoupServer *server, SoupMessage *msg,
soup_message_set_status (msg, SOUP_STATUS_OK);
soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CHUNKED);
- if (!soup_header_contains (options, "empty")) {
- soup_message_body_append (msg->response_body,
- SOUP_MEMORY_TAKE, contents, length);
- }
+ if (!soup_header_contains (options, "empty"))
+ soup_message_body_append_buffer (msg->response_body, response);
+ soup_buffer_free (response);
if (soup_header_contains (options, "trailing-junk")) {
soup_message_body_append (msg->response_body, SOUP_MEMORY_COPY,
@@ -118,6 +116,20 @@ server_callback (SoupServer *server, SoupMessage *msg,
soup_message_body_complete (msg->response_body);
}
+typedef struct {
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupRequest *req;
+ SoupBuffer *response;
+} CodingTestData;
+
+typedef enum {
+ CODING_TEST_DEFAULT = 0,
+ CODING_TEST_NO_DECODER = (1 << 0),
+ CODING_TEST_REQUEST_API = (1 << 1),
+ CODING_TEST_EMPTY = (1 << 2)
+} CodingTestType;
+
typedef enum {
NO_CHECK,
EXPECT_DECODED,
@@ -125,184 +137,189 @@ typedef enum {
} MessageContentStatus;
static void
-check_response (SoupMessage *msg,
+check_response (CodingTestData *data,
const char *expected_encoding,
const char *expected_content_type,
- MessageContentStatus status)
+ MessageContentStatus status,
+ GByteArray *body)
{
const char *coding, *type;
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " Unexpected status %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (data->msg, SOUP_STATUS_OK);
- coding = soup_message_headers_get_one (msg->response_headers, "Content-Encoding");
- if (expected_encoding) {
- if (!coding || g_ascii_strcasecmp (coding, expected_encoding) != 0) {
- debug_printf (1, " Unexpected Content-Encoding: %s\n",
- coding ? coding : "(none)");
- errors++;
- }
- } else {
- if (coding) {
- debug_printf (1, " Unexpected Content-Encoding: %s\n",
- coding);
- errors++;
- }
- }
+ coding = soup_message_headers_get_one (data->msg->response_headers, "Content-Encoding");
+ g_assert_cmpstr (coding, ==, expected_encoding);
if (status != NO_CHECK) {
- if (status == EXPECT_DECODED) {
- if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_CONTENT_DECODED)) {
- debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED not set!\n");
- errors++;
- }
- } else {
- if (soup_message_get_flags (msg) & SOUP_MESSAGE_CONTENT_DECODED) {
- debug_printf (1, " SOUP_MESSAGE_CONTENT_DECODED set!\n");
- errors++;
- }
- }
+ if (status == EXPECT_DECODED)
+ g_assert_true (soup_message_get_flags (data->msg) & SOUP_MESSAGE_CONTENT_DECODED);
+ else
+ g_assert_false (soup_message_get_flags (data->msg) & SOUP_MESSAGE_CONTENT_DECODED);
}
- type = soup_message_headers_get_one (msg->response_headers, "Content-Type");
- if (!type || g_ascii_strcasecmp (type, expected_content_type) != 0) {
- debug_printf (1, " Unexpected Content-Type: %s\n",
- type ? type : "(none)");
- errors++;
+ type = soup_message_headers_get_one (data->msg->response_headers, "Content-Type");
+ g_assert_cmpstr (type, ==, expected_content_type);
+
+ if (body) {
+ soup_assert_cmpmem (body->data,
+ body->len,
+ data->response->data,
+ data->response->length);
+ } else {
+ soup_assert_cmpmem (data->msg->response_body->data,
+ data->msg->response_body->length,
+ data->response->data,
+ data->response->length);
}
}
static void
-check_msg_bodies (SoupMessage *msg1,
- SoupMessage *msg2,
- const char *msg1_type,
- const char *msg2_type)
+setup_coding_test (CodingTestData *data, gconstpointer test_data)
{
- if (msg1->response_body->length != msg2->response_body->length) {
- debug_printf (1, " Message length mismatch: %lu (%s) vs %lu (%s)\n",
- (gulong)msg1->response_body->length,
- msg1_type,
- (gulong)msg2->response_body->length,
- msg2_type);
- errors++;
- } else if (memcmp (msg1->response_body->data,
- msg2->response_body->data,
- msg1->response_body->length) != 0) {
- debug_printf (1, " Message data mismatch (%s/%s)\n",
- msg1_type, msg2_type);
- errors++;
+ CodingTestType test_type = GPOINTER_TO_INT (test_data);
+ SoupMessage *msg;
+ SoupURI *uri;
+
+ data->session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+
+ uri = soup_uri_new_with_base (base_uri, "/mbox");
+
+ if (test_type & CODING_TEST_EMPTY)
+ data->response = soup_buffer_new (SOUP_MEMORY_STATIC, "", 0);
+ else {
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_session_send_message (data->session, msg);
+
+ data->response = soup_message_body_flatten (msg->response_body);
+ g_object_unref (msg);
}
+
+ if (test_type & CODING_TEST_REQUEST_API) {
+ SoupRequestHTTP *reqh;
+
+ reqh = soup_session_request_http_uri (data->session, "GET", uri, NULL);
+ data->req = SOUP_REQUEST (reqh);
+ data->msg = soup_request_http_get_message (reqh);
+ } else
+ data->msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ if (! (test_type & CODING_TEST_NO_DECODER))
+ soup_session_add_feature_by_type (data->session, SOUP_TYPE_CONTENT_DECODER);
}
static void
-do_coding_test (void)
+teardown_coding_test (CodingTestData *data, gconstpointer test_data)
{
- SoupSession *session;
- SoupMessage *msg, *msgz, *msgj, *msge, *msgzl, *msgzlj, *msgzle, *msgzlr, *msgzlre;
- SoupURI *uri;
+ soup_buffer_free (data->response);
- debug_printf (1, "SoupMessage tests\n");
+ g_clear_object (&data->req);
+ g_object_unref (data->msg);
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- uri = soup_uri_new_with_base (base_uri, "/mbox");
+ soup_test_session_abort_unref (data->session);
+}
+
+static void
+do_coding_test_plain (CodingTestData *data, gconstpointer test_data)
+{
+ soup_session_send_message (data->session, data->msg);
+ check_response (data, NULL, "text/plain", EXPECT_NOT_DECODED, NULL);
+}
+
+static void
+do_coding_test_gzip (CodingTestData *data, gconstpointer test_data)
+{
+ soup_session_send_message (data->session, data->msg);
+ check_response (data, "gzip", "text/plain", EXPECT_DECODED, NULL);
+}
- /* Plain text data, no claim */
- debug_printf (1, " GET /mbox, plain\n");
- msg = soup_message_new_from_uri ("GET", uri);
- soup_session_send_message (session, msg);
- check_response (msg, NULL, "text/plain", EXPECT_NOT_DECODED);
-
- /* Plain text data, claim gzip */
- debug_printf (1, " GET /mbox, Accept-Encoding: gzip\n");
- soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
- msgz = soup_message_new_from_uri ("GET", uri);
- soup_session_send_message (session, msgz);
- check_response (msgz, "gzip", "text/plain", EXPECT_DECODED);
- check_msg_bodies (msg, msgz, "plain", "compressed");
-
- /* Plain text data, claim gzip w/ junk */
- debug_printf (1, " GET /mbox, Accept-Encoding: gzip, plus trailing junk\n");
- msgj = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msgj->request_headers,
+static void
+do_coding_test_gzip_with_junk (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("606352");
+ g_test_bug ("676477");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "trailing-junk");
- soup_session_send_message (session, msgj);
- check_response (msgj, "gzip", "text/plain", EXPECT_DECODED);
- check_msg_bodies (msg, msgj, "plain", "compressed w/ junk");
-
- /* Plain text data, claim gzip with server error */
- debug_printf (1, " GET /mbox, Accept-Encoding: gzip, with server error\n");
- msge = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msge->request_headers,
+
+ soup_session_send_message (data->session, data->msg);
+ check_response (data, "gzip", "text/plain", EXPECT_DECODED, NULL);
+}
+
+static void
+do_coding_test_gzip_bad_server (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("613361");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "force-encode");
- soup_session_send_message (session, msge);
- check_response (msge, "gzip", "text/plain", EXPECT_NOT_DECODED);
+
+ soup_session_send_message (data->session, data->msg);
/* Failed content-decoding should have left the body untouched
* from what the server sent... which happens to be the
* uncompressed data.
*/
- check_msg_bodies (msg, msge, "plain", "mis-encoded");
+ check_response (data, "gzip", "text/plain", EXPECT_NOT_DECODED, NULL);
+}
- /* Plain text data, claim deflate */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate\n");
- msgzl = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msgzl->request_headers,
+static void
+do_coding_test_deflate (CodingTestData *data, gconstpointer test_data)
+{
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "prefer-deflate-zlib");
- soup_session_send_message (session, msgzl);
- check_response (msgzl, "deflate", "text/plain", EXPECT_DECODED);
- check_msg_bodies (msg, msgzl, "plain", "compressed");
-
- /* Plain text data, claim deflate w/ junk */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate, plus trailing junk\n");
- msgzlj = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msgzlj->request_headers,
+ soup_session_send_message (data->session, data->msg);
+
+ check_response (data, "deflate", "text/plain", EXPECT_DECODED, NULL);
+}
+
+static void
+do_coding_test_deflate_with_junk (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("606352");
+ g_test_bug ("676477");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "prefer-deflate-zlib, trailing-junk");
- soup_session_send_message (session, msgzlj);
- check_response (msgzlj, "deflate", "text/plain", EXPECT_DECODED);
- check_msg_bodies (msg, msgzlj, "plain", "compressed w/ junk");
-
- /* Plain text data, claim deflate with server error */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate, with server error\n");
- msgzle = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msgzle->request_headers,
+ soup_session_send_message (data->session, data->msg);
+
+ check_response (data, "deflate", "text/plain", EXPECT_DECODED, NULL);
+}
+
+static void
+do_coding_test_deflate_bad_server (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("613361");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "force-encode, prefer-deflate-zlib");
- soup_session_send_message (session, msgzle);
- check_response (msgzle, "deflate", "text/plain", EXPECT_NOT_DECODED);
- check_msg_bodies (msg, msgzle, "plain", "mis-encoded");
-
- /* Plain text data, claim deflate (no zlib headers)*/
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data)\n");
- msgzlr = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msgzlr->request_headers,
+ soup_session_send_message (data->session, data->msg);
+
+ check_response (data, "deflate", "text/plain", EXPECT_NOT_DECODED, NULL);
+}
+
+static void
+do_coding_test_deflate_raw (CodingTestData *data, gconstpointer test_data)
+{
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "prefer-deflate-raw");
- soup_session_send_message (session, msgzlr);
- check_response (msgzlr, "deflate", "text/plain", EXPECT_DECODED);
- check_msg_bodies (msg, msgzlr, "plain", "compressed");
-
- /* Plain text data, claim deflate with server error */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data), with server error\n");
- msgzlre = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msgzlre->request_headers,
+ soup_session_send_message (data->session, data->msg);
+
+ check_response (data, "deflate", "text/plain", EXPECT_DECODED, NULL);
+}
+
+static void
+do_coding_test_deflate_raw_bad_server (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("613361");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "force-encode, prefer-deflate-raw");
- soup_session_send_message (session, msgzlre);
- check_response (msgzlre, "deflate", "text/plain", EXPECT_NOT_DECODED);
- check_msg_bodies (msg, msgzlre, "plain", "mis-encoded");
-
- g_object_unref (msg);
- g_object_unref (msgzlre);
- g_object_unref (msgzlr);
- g_object_unref (msgzlj);
- g_object_unref (msgzle);
- g_object_unref (msgzl);
- g_object_unref (msgz);
- g_object_unref (msgj);
- g_object_unref (msge);
- soup_uri_free (uri);
+ soup_session_send_message (data->session, data->msg);
- soup_test_session_abort_unref (session);
+ check_response (data, "deflate", "text/plain", EXPECT_NOT_DECODED, NULL);
}
static void
@@ -313,38 +330,29 @@ read_finished (GObject *stream, GAsyncResult *result, gpointer user_data)
*nread = g_input_stream_read_finish (G_INPUT_STREAM (stream),
result, &error);
- if (error) {
- debug_printf (1, " Error reading: %s\n",
- error->message);
- g_error_free (error);
- errors++;
- }
+ g_assert_no_error (error);
+ g_clear_error (&error);
}
-static GByteArray *
-do_single_coding_req_test (SoupRequest *req,
+static void
+do_single_coding_req_test (CodingTestData *data,
const char *expected_encoding,
const char *expected_content_type,
MessageContentStatus status)
{
GInputStream *stream;
- SoupMessage *msg;
- GByteArray *data;
+ GByteArray *body;
guchar buf[1024];
gssize nread;
GError *error = NULL;
- data = g_byte_array_new ();
-
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
+ body = g_byte_array_new ();
- stream = soup_test_request_send_async_as_sync (req, NULL, &error);
- if (error) {
- debug_printf (1, " Error sending request: %s\n",
- error->message);
+ stream = soup_test_request_send (data->req, NULL, 0, &error);
+ if (!stream) {
+ g_assert_no_error (error);
g_error_free (error);
- errors++;
- return data;
+ return;
}
do {
@@ -356,208 +364,126 @@ do_single_coding_req_test (SoupRequest *req,
g_main_context_iteration (NULL, TRUE);
if (nread > 0)
- g_byte_array_append (data, buf, nread);
+ g_byte_array_append (body, buf, nread);
} while (nread > 0);
- soup_test_stream_close_async_as_sync (stream, NULL, &error);
- if (error) {
- debug_printf (1, " error closing stream: %s\n",
- error->message);
- g_error_free (error);
- errors++;
- }
+ soup_test_request_close_stream (data->req, stream, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ g_object_unref (stream);
- check_response (msg, expected_encoding, expected_content_type, status);
- g_object_unref (msg);
+ check_response (data, expected_encoding, expected_content_type, status, body);
+ g_byte_array_free (body, TRUE);
+}
- return data;
+static void
+do_coding_req_test_plain (CodingTestData *data, gconstpointer test_data)
+{
+ do_single_coding_req_test (data, NULL, "text/plain", EXPECT_NOT_DECODED);
}
static void
-check_req_bodies (GByteArray *body1,
- GByteArray *body2,
- const char *msg1_type,
- const char *msg2_type)
+do_coding_req_test_gzip (CodingTestData *data, gconstpointer test_data)
{
- if (body1->len != body2->len) {
- debug_printf (1, " Message length mismatch: %lu (%s) vs %lu (%s)\n",
- (gulong)body1->len, msg1_type,
- (gulong)body2->len, msg2_type);
- errors++;
- } else if (memcmp (body1->data, body2->data, body1->len) != 0) {
- debug_printf (1, " Message data mismatch (%s/%s)\n",
- msg1_type, msg2_type);
- errors++;
- }
+ do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_DECODED);
}
static void
-do_coding_req_test (void)
+do_coding_req_test_gzip_with_junk (CodingTestData *data, gconstpointer test_data)
{
- SoupSession *session;
- SoupRequester *requester;
- SoupRequest *req;
- SoupMessage *msg;
- SoupURI *uri;
- GByteArray *plain, *cmp;
+ g_test_bug ("606352");
+ g_test_bug ("676477");
- debug_printf (1, "\nSoupRequest tests\n");
+ soup_message_headers_append (data->msg->request_headers,
+ "X-Test-Options", "trailing-junk");
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
- NULL);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- uri = soup_uri_new_with_base (base_uri, "/mbox");
+ do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_DECODED);
+}
- /* Plain text data, no claim */
- debug_printf (1, " GET /mbox, plain\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- plain = do_single_coding_req_test (req, NULL, "text/plain", EXPECT_NOT_DECODED);
- g_object_unref (req);
-
- /* Plain text data, claim gzip */
- debug_printf (1, " GET /mbox, Accept-Encoding: gzip\n");
- soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER);
- req = soup_requester_request_uri (requester, uri, NULL);
- cmp = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_DECODED);
- check_req_bodies (plain, cmp, "plain", "compressed");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
-
- /* Plain text data, claim gzip w/ junk */
- debug_printf (1, " GET /mbox, Accept-Encoding: gzip, plus trailing junk\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
- "X-Test-Options", "trailing-junk");
- cmp = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_DECODED);
- check_req_bodies (plain, cmp, "plain", "compressed w/ junk");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
-
- /* Plain text data, claim gzip with server error */
- debug_printf (1, " GET /mbox, Accept-Encoding: gzip, with server error\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
+static void
+do_coding_req_test_gzip_bad_server (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("613361");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "force-encode");
- cmp = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_NOT_DECODED);
+ do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_NOT_DECODED);
+}
- /* Failed content-decoding should have left the body untouched
- * from what the server sent... which happens to be the
- * uncompressed data.
- */
- check_req_bodies (plain, cmp, "plain", "mis-encoded");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
-
- /* Plain text data, claim deflate */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
+static void
+do_coding_req_test_deflate (CodingTestData *data, gconstpointer test_data)
+{
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "prefer-deflate-zlib");
- cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_DECODED);
- check_req_bodies (plain, cmp, "plain", "compressed");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
-
- /* Plain text data, claim deflate w/ junk */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate, plus trailing junk\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
+ do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_DECODED);
+}
+
+static void
+do_coding_req_test_deflate_with_junk (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("606352");
+ g_test_bug ("676477");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "prefer-deflate-zlib, trailing-junk");
- cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_DECODED);
- check_req_bodies (plain, cmp, "plain", "compressed w/ junk");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
-
- /* Plain text data, claim deflate with server error */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate, with server error\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
- "X-Test-Options", "force-encode, prefer-deflate-zlib");
- cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_NOT_DECODED);
- check_req_bodies (plain, cmp, "plain", "mis-encoded");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
-
- /* Plain text data, claim deflate (no zlib headers)*/
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data)\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
- "X-Test-Options", "prefer-deflate-raw");
- cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_DECODED);
- check_req_bodies (plain, cmp, "plain", "compressed");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
-
- /* Plain text data, claim deflate with server error */
- debug_printf (1, " GET /mbox, Accept-Encoding: deflate (raw data), with server error\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
- "X-Test-Options", "force-encode, prefer-deflate-raw");
- cmp = do_single_coding_req_test (req, "deflate", "text/plain", EXPECT_NOT_DECODED);
- check_req_bodies (plain, cmp, "plain", "mis-encoded");
- g_byte_array_free (cmp, TRUE);
- g_object_unref (req);
+ do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_DECODED);
+}
- g_byte_array_free (plain, TRUE);
- soup_uri_free (uri);
+static void
+do_coding_req_test_deflate_bad_server (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("613361");
- soup_test_session_abort_unref (session);
+ soup_message_headers_append (data->msg->request_headers,
+ "X-Test-Options", "force-encode, prefer-deflate-zlib");
+ do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_NOT_DECODED);
}
static void
-do_coding_empty_test (void)
+do_coding_req_test_deflate_raw (CodingTestData *data, gconstpointer test_data)
{
- SoupSession *session;
- SoupMessage *msg;
- SoupURI *uri;
- SoupRequester *requester;
- SoupRequest *req;
- GByteArray *body;
+ soup_message_headers_append (data->msg->request_headers,
+ "X-Test-Options", "prefer-deflate-raw");
+ do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_DECODED);
+}
- debug_printf (1, "\nEmpty allegedly-encoded body test\n");
+static void
+do_coding_req_test_deflate_raw_bad_server (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("613361");
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
- SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
- NULL);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- uri = soup_uri_new_with_base (base_uri, "/mbox");
+ soup_message_headers_append (data->msg->request_headers,
+ "X-Test-Options", "force-encode, prefer-deflate-raw");
+ do_single_coding_req_test (data, "deflate", "text/plain", EXPECT_NOT_DECODED);
+}
- debug_printf (1, " SoupMessage\n");
- msg = soup_message_new_from_uri ("GET", uri);
- soup_message_headers_append (msg->request_headers,
- "X-Test-Options", "empty");
- soup_session_send_message (session, msg);
- check_response (msg, "gzip", "text/plain", EXPECT_NOT_DECODED);
- g_object_unref (msg);
-
- debug_printf (1, " SoupRequest\n");
- req = soup_requester_request_uri (requester, uri, NULL);
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- soup_message_headers_append (msg->request_headers,
+static void
+do_coding_msg_empty_test (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("697527");
+
+ soup_message_headers_append (data->msg->request_headers,
"X-Test-Options", "empty");
- g_object_unref (msg);
- body = do_single_coding_req_test (req, "gzip", "text/plain", EXPECT_NOT_DECODED);
- g_byte_array_free (body, TRUE);
- g_object_unref (req);
+ soup_session_send_message (data->session, data->msg);
- soup_test_session_abort_unref (session);
+ check_response (data, "gzip", "text/plain", EXPECT_NOT_DECODED, NULL);
}
+static void
+do_coding_req_empty_test (CodingTestData *data, gconstpointer test_data)
+{
+ g_test_bug ("697527");
+
+ soup_message_headers_append (data->msg->request_headers,
+ "X-Test-Options", "empty");
+ do_single_coding_req_test (data, "gzip", "text/plain", EXPECT_NOT_DECODED);
+}
int
main (int argc, char **argv)
{
+ int ret;
+
test_init (argc, argv, NULL);
server = soup_test_server_new (TRUE);
@@ -565,13 +491,74 @@ main (int argc, char **argv)
base_uri = soup_uri_new ("http://127.0.0.1/");
soup_uri_set_port (base_uri, soup_server_get_port (server));
- do_coding_test ();
- do_coding_req_test ();
- do_coding_empty_test ();
+ g_test_add ("/coding/message/plain", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_NO_DECODER),
+ setup_coding_test, do_coding_test_plain, teardown_coding_test);
+ g_test_add ("/coding/message/gzip", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_gzip, teardown_coding_test);
+ g_test_add ("/coding/message/gzip/with-junk", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_gzip_with_junk, teardown_coding_test);
+ g_test_add ("/coding/message/gzip/bad-server", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_gzip_bad_server, teardown_coding_test);
+ g_test_add ("/coding/message/deflate", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_deflate, teardown_coding_test);
+ g_test_add ("/coding/message/deflate/with-junk", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_deflate_with_junk, teardown_coding_test);
+ g_test_add ("/coding/message/deflate/bad-server", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_deflate_bad_server, teardown_coding_test);
+ g_test_add ("/coding/message/deflate-raw", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_deflate_raw, teardown_coding_test);
+ g_test_add ("/coding/message/deflate-raw/bad-server", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_deflate_raw_bad_server, teardown_coding_test);
+
+ g_test_add ("/coding/request/plain", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_NO_DECODER | CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_plain, teardown_coding_test);
+ g_test_add ("/coding/request/gzip", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_gzip, teardown_coding_test);
+ g_test_add ("/coding/request/gzip/with-junk", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_gzip_with_junk, teardown_coding_test);
+ g_test_add ("/coding/request/gzip/bad-server", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_gzip_bad_server, teardown_coding_test);
+ g_test_add ("/coding/request/deflate", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_deflate, teardown_coding_test);
+ g_test_add ("/coding/request/deflate/with-junk", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_deflate_with_junk, teardown_coding_test);
+ g_test_add ("/coding/request/deflate/bad-server", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_deflate_bad_server, teardown_coding_test);
+ g_test_add ("/coding/request/deflate-raw", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_deflate_raw, teardown_coding_test);
+ g_test_add ("/coding/request/deflate-raw/bad-server", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API),
+ setup_coding_test, do_coding_req_test_deflate_raw_bad_server, teardown_coding_test);
+
+ g_test_add ("/coding/message/empty", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_EMPTY),
+ setup_coding_test, do_coding_msg_empty_test, teardown_coding_test);
+ g_test_add ("/coding/request/empty", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_REQUEST_API | CODING_TEST_EMPTY),
+ setup_coding_test, do_coding_req_empty_test, teardown_coding_test);
+
+ ret = g_test_run ();
soup_uri_free (base_uri);
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/connection-test.c b/tests/connection-test.c
index ddf0c988..8f8c74be 100644
--- a/tests/connection-test.c
+++ b/tests/connection-test.c
@@ -5,6 +5,8 @@
#include "test-utils.h"
+#include <gio/gnetworking.h>
+
SoupServer *server;
SoupURI *base_uri;
GMutex server_mutex;
@@ -164,7 +166,7 @@ do_content_length_framing_test (void)
SoupURI *request_uri;
goffset declared_length;
- debug_printf (1, "\nInvalid Content-Length framing tests\n");
+ g_test_bug ("611481");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -172,21 +174,14 @@ do_content_length_framing_test (void)
request_uri = soup_uri_new_with_base (base_uri, "/content-length/long");
msg = soup_message_new_from_uri ("GET", request_uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- } else {
- declared_length = soup_message_headers_get_content_length (msg->response_headers);
- debug_printf (2, " Content-Length: %lu, body: %s\n",
- (gulong)declared_length, msg->response_body->data);
- if (msg->response_body->length >= declared_length) {
- debug_printf (1, " Body length %lu >= declared length %lu\n",
- (gulong)msg->response_body->length,
- (gulong)declared_length);
- errors++;
- }
- }
+
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+ declared_length = soup_message_headers_get_content_length (msg->response_headers);
+ debug_printf (2, " Content-Length: %lu, body: %s\n",
+ (gulong)declared_length, msg->response_body->data);
+ g_assert_cmpint (msg->response_body->length, <, declared_length);
+
soup_uri_free (request_uri);
g_object_unref (msg);
@@ -194,21 +189,12 @@ do_content_length_framing_test (void)
request_uri = soup_uri_new_with_base (base_uri, "/content-length/noclose");
msg = soup_message_new_from_uri ("GET", request_uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- } else {
- declared_length = soup_message_headers_get_content_length (msg->response_headers);
- debug_printf (2, " Content-Length: %lu, body: %s\n",
- (gulong)declared_length, msg->response_body->data);
- if (msg->response_body->length != declared_length) {
- debug_printf (1, " Body length %lu != declared length %lu\n",
- (gulong)msg->response_body->length,
- (gulong)declared_length);
- errors++;
- }
- }
+
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+ declared_length = soup_message_headers_get_content_length (msg->response_headers);
+ g_assert_cmpint (msg->response_body->length, ==, declared_length);
+
soup_uri_free (request_uri);
g_object_unref (msg);
@@ -232,12 +218,11 @@ request_started_socket_collector (SoupSession *session, SoupMessage *msg,
* two consecutive sockets.
*/
sockets[i] = g_object_ref (socket);
- return;
+ break;
}
}
- debug_printf (1, " socket queue overflowed!\n");
- errors++;
+ soup_test_assert (i < 4, "socket queue overflowed");
}
static void
@@ -257,14 +242,10 @@ do_timeout_test_for_session (SoupSession *session)
msg = soup_message_new_from_uri ("GET", timeout_uri);
soup_uri_free (timeout_uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
if (sockets[1]) {
- debug_printf (1, " Message was retried??\n");
- errors++;
+ soup_test_assert (sockets[1] == NULL, "Message was retried");
sockets[1] = sockets[2] = sockets[3] = NULL;
}
g_object_unref (msg);
@@ -272,24 +253,16 @@ do_timeout_test_for_session (SoupSession *session)
debug_printf (1, " Second message\n");
msg = soup_message_new_from_uri ("GET", base_uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- if (sockets[1] != sockets[0]) {
- debug_printf (1, " Message was not retried on existing connection\n");
- errors++;
- } else if (!sockets[2]) {
- debug_printf (1, " Message was not retried after disconnect\n");
- errors++;
- } else if (sockets[2] == sockets[1]) {
- debug_printf (1, " Message was retried on closed connection??\n");
- errors++;
- } else if (sockets[3]) {
- debug_printf (1, " Message was retried again??\n");
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+ soup_test_assert (sockets[1] == sockets[0],
+ "Message was not retried on existing connection");
+ soup_test_assert (sockets[2] != NULL,
+ "Message was not retried after disconnect");
+ soup_test_assert (sockets[2] != sockets[1],
+ "Message was retried on closed connection");
+ soup_test_assert (sockets[3] == NULL,
+ "Message was retried again");
g_object_unref (msg);
for (i = 0; sockets[i]; i++)
@@ -299,7 +272,6 @@ do_timeout_test_for_session (SoupSession *session)
static void
do_timeout_req_test_for_session (SoupSession *session)
{
- SoupRequester *requester;
SoupRequest *req;
SoupMessage *msg;
GInputStream *stream;
@@ -308,94 +280,67 @@ do_timeout_req_test_for_session (SoupSession *session)
GError *error = NULL;
int i;
- requester = soup_requester_new ();
- soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
- g_object_unref (requester);
-
g_signal_connect (session, "request-started",
G_CALLBACK (request_started_socket_collector),
&sockets);
debug_printf (1, " First request\n");
timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent");
- req = soup_requester_request_uri (requester, timeout_uri, NULL);
+ req = soup_session_request_uri (session, timeout_uri, NULL);
soup_uri_free (timeout_uri);
- if (SOUP_IS_SESSION_SYNC (session))
- stream = soup_request_send (req, NULL, &error);
- else
- stream = soup_test_request_send_async_as_sync (req, NULL, &error);
-
- if (!stream) {
- debug_printf (1, " Unexpected error on send: %s\n",
- error->message);
- errors++;
+ stream = soup_test_request_send (req, NULL, 0, &error);
+ if (error) {
+ g_assert_no_error (error);
g_clear_error (&error);
} else {
- if (SOUP_IS_SESSION_SYNC (session))
- g_input_stream_close (stream, NULL, &error);
- else
- soup_test_stream_close_async_as_sync (stream, NULL, &error);
+ soup_test_request_read_all (req, stream, NULL, &error);
+ if (error) {
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ }
+
+ soup_test_request_close_stream (req, stream, NULL, &error);
if (error) {
- debug_printf (1, " Unexpected error on close: %s\n",
- error->message);
- errors++;
+ g_assert_no_error (error);
g_clear_error (&error);
}
+ g_object_unref (stream);
}
if (sockets[1]) {
- debug_printf (1, " Message was retried??\n");
- errors++;
+ soup_test_assert (sockets[1] == NULL, "Message was retried");
sockets[1] = sockets[2] = sockets[3] = NULL;
}
g_object_unref (req);
debug_printf (1, " Second request\n");
- req = soup_requester_request_uri (requester, base_uri, NULL);
+ req = soup_session_request_uri (session, base_uri, NULL);
- if (SOUP_IS_SESSION_SYNC (session))
- stream = soup_request_send (req, NULL, &error);
- else
- stream = soup_test_request_send_async_as_sync (req, NULL, &error);
-
- if (!stream) {
- debug_printf (1, " Unexpected error on send: %s\n",
- error->message);
- errors++;
+ stream = soup_test_request_send (req, NULL, 0, &error);
+ if (error) {
+ g_assert_no_error (error);
g_clear_error (&error);
} else {
- if (SOUP_IS_SESSION_SYNC (session))
- g_input_stream_close (stream, NULL, &error);
- else
- soup_test_stream_close_async_as_sync (stream, NULL, &error);
+ soup_test_request_close_stream (req, stream, NULL, &error);
if (error) {
- debug_printf (1, " Unexpected error on close: %s\n",
- error->message);
- errors++;
+ g_assert_no_error (error);
g_clear_error (&error);
}
+ g_object_unref (stream);
}
msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- if (sockets[1] != sockets[0]) {
- debug_printf (1, " Message was not retried on existing connection\n");
- errors++;
- } else if (!sockets[2]) {
- debug_printf (1, " Message was not retried after disconnect\n");
- errors++;
- } else if (sockets[2] == sockets[1]) {
- debug_printf (1, " Message was retried on closed connection??\n");
- errors++;
- } else if (sockets[3]) {
- debug_printf (1, " Message was retried again??\n");
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+ soup_test_assert (sockets[1] == sockets[0],
+ "Message was not retried on existing connection");
+ soup_test_assert (sockets[2] != NULL,
+ "Message was not retried after disconnect");
+ soup_test_assert (sockets[2] != sockets[1],
+ "Message was retried on closed connection");
+ soup_test_assert (sockets[3] == NULL,
+ "Message was retried again");
g_object_unref (msg);
g_object_unref (req);
@@ -408,13 +353,14 @@ do_persistent_connection_timeout_test (void)
{
SoupSession *session;
- debug_printf (1, "\nUnexpected timing out of persistent connections\n");
+ g_test_bug ("631525");
debug_printf (1, " Async session, message API\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
do_timeout_test_for_session (session);
soup_test_session_abort_unref (session);
+ debug_printf (1, " Async session, request API\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
@@ -426,6 +372,7 @@ do_persistent_connection_timeout_test (void)
do_timeout_test_for_session (session);
soup_test_session_abort_unref (session);
+ debug_printf (1, " Sync session, request API\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
do_timeout_req_test_for_session (session);
soup_test_session_abort_unref (session);
@@ -491,26 +438,18 @@ do_max_conns_test_for_session (SoupSession *session)
}
g_main_loop_run (max_conns_loop);
- if (msgs_done != MAX_CONNS) {
- debug_printf (1, " Queued %d connections out of max %d?",
- msgs_done, MAX_CONNS);
- errors++;
- }
+ g_assert_cmpint (msgs_done, ==, MAX_CONNS);
g_signal_handlers_disconnect_by_func (session, max_conns_request_started, NULL);
msgs_done = 0;
g_idle_add (idle_start_server, NULL);
+ if (quit_loop_timeout)
+ g_source_remove (quit_loop_timeout);
quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL);
g_main_loop_run (max_conns_loop);
- for (i = 0; i < TEST_CONNS; i++) {
- if (!SOUP_STATUS_IS_SUCCESSFUL (msgs[i]->status_code)) {
- debug_printf (1, " Message %d failed? %d %s\n",
- i, msgs[i]->status_code,
- msgs[i]->reason_phrase ? msgs[i]->reason_phrase : "-");
- errors++;
- }
- }
+ for (i = 0; i < TEST_CONNS; i++)
+ soup_test_assert_message_status (msgs[i], SOUP_STATUS_OK);
if (msgs_done != TEST_CONNS) {
/* Clean up so we don't get a spurious "Leaked
@@ -522,8 +461,10 @@ do_max_conns_test_for_session (SoupSession *session)
}
g_main_loop_unref (max_conns_loop);
- if (quit_loop_timeout)
+ if (quit_loop_timeout) {
g_source_remove (quit_loop_timeout);
+ quit_loop_timeout = 0;
+ }
for (i = 0; i < TEST_CONNS; i++)
g_object_unref (msgs[i]);
@@ -534,7 +475,7 @@ do_max_conns_test (void)
{
SoupSession *session;
- debug_printf (1, "\nExceeding max-conns\n");
+ g_test_bug ("634422");
debug_printf (1, " Async session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
@@ -551,8 +492,6 @@ do_max_conns_test (void)
soup_test_session_abort_unref (session);
}
-GMainLoop *loop;
-
static void
np_request_started (SoupSession *session, SoupMessage *msg,
SoupSocket *socket, gpointer user_data)
@@ -568,10 +507,14 @@ np_request_unqueued (SoupSession *session, SoupMessage *msg,
{
SoupSocket *socket = *(SoupSocket **)user_data;
- if (soup_socket_is_connected (socket)) {
- debug_printf (1, " socket is still connected\n");
- errors++;
- }
+ g_assert_false (soup_socket_is_connected (socket));
+}
+
+static void
+np_request_finished (SoupSession *session, SoupMessage *msg,
+ gpointer user_data)
+{
+ GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
@@ -581,6 +524,7 @@ do_non_persistent_test_for_session (SoupSession *session)
{
SoupMessage *msg;
SoupSocket *socket = NULL;
+ GMainLoop *loop;
loop = g_main_loop_new (NULL, FALSE);
@@ -594,15 +538,15 @@ do_non_persistent_test_for_session (SoupSession *session)
msg = soup_message_new_from_uri ("GET", base_uri);
soup_message_headers_append (msg->request_headers, "Connection", "close");
g_object_ref (msg);
- soup_session_queue_message (session, msg, NULL, NULL);
+ soup_session_queue_message (session, msg,
+ np_request_finished, loop);
g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
g_object_unref (msg);
+ g_object_unref (socket);
}
static void
@@ -610,7 +554,7 @@ do_non_persistent_connection_test (void)
{
SoupSession *session;
- debug_printf (1, "\nNon-persistent connections are closed immediately\n");
+ g_test_bug ("578990");
debug_printf (1, " Async session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -637,14 +581,9 @@ do_non_idempotent_test_for_session (SoupSession *session)
debug_printf (2, " GET\n");
msg = soup_message_new_from_uri ("GET", base_uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
if (sockets[1]) {
- debug_printf (1, " Message was retried??\n");
- errors++;
+ soup_test_assert (sockets[1] == NULL, "Message was retried");
sockets[1] = sockets[2] = sockets[3] = NULL;
}
g_object_unref (msg);
@@ -652,19 +591,12 @@ do_non_idempotent_test_for_session (SoupSession *session)
debug_printf (2, " POST\n");
msg = soup_message_new_from_uri ("POST", base_uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- if (sockets[1] == sockets[0]) {
- debug_printf (1, " Message was sent on existing connection!\n");
- errors++;
- }
- if (sockets[2]) {
- debug_printf (1, " Too many connections used...\n");
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ soup_test_assert (sockets[1] != sockets[0],
+ "Message was sent on existing connection");
+ soup_test_assert (sockets[2] == NULL,
+ "Too many connections used");
+
g_object_unref (msg);
for (i = 0; sockets[i]; i++)
@@ -676,8 +608,6 @@ do_non_idempotent_connection_test (void)
{
SoupSession *session;
- debug_printf (1, "\nNon-idempotent methods are always sent on new connections\n");
-
debug_printf (1, " Async session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
do_non_idempotent_test_for_session (session);
@@ -689,25 +619,254 @@ do_non_idempotent_connection_test (void)
soup_test_session_abort_unref (session);
}
+#define HTTP_SERVER "http://127.0.0.1:47524"
+#define HTTPS_SERVER "https://127.0.0.1:47525"
+#define HTTP_PROXY "http://127.0.0.1:47526"
+
+static SoupConnectionState state_transitions[] = {
+ /* NEW -> */ SOUP_CONNECTION_CONNECTING,
+ /* CONNECTING -> */ SOUP_CONNECTION_IN_USE,
+ /* IDLE -> */ SOUP_CONNECTION_DISCONNECTED,
+ /* IN_USE -> */ SOUP_CONNECTION_IDLE,
+
+ /* REMOTE_DISCONNECTED */ -1,
+ /* DISCONNECTED */ -1,
+};
+
+static const char *state_names[] = {
+ "NEW", "CONNECTING", "IDLE", "IN_USE",
+ "REMOTE_DISCONNECTED", "DISCONNECTED"
+};
+
+static void
+connection_state_changed (GObject *object, GParamSpec *param,
+ gpointer user_data)
+{
+ SoupConnectionState *state = user_data;
+ SoupConnectionState new_state;
+
+ g_object_get (object, "state", &new_state, NULL);
+ debug_printf (2, " %s -> %s\n",
+ state_names[*state], state_names[new_state]);
+ soup_test_assert (state_transitions[*state] == new_state,
+ "Unexpected transition: %s -> %s\n",
+ state_names[*state], state_names[new_state]);
+ *state = new_state;
+}
+
+static void
+connection_created (SoupSession *session, GObject *conn,
+ gpointer user_data)
+{
+ SoupConnectionState *state = user_data;
+
+ g_object_get (conn, "state", state, NULL);
+ g_assert_cmpint (*state, ==, SOUP_CONNECTION_NEW);
+
+ g_signal_connect (conn, "notify::state",
+ G_CALLBACK (connection_state_changed),
+ state);
+}
+
+static void
+do_one_connection_state_test (SoupSession *session, const char *uri)
+{
+ SoupMessage *msg;
+
+ msg = soup_message_new ("GET", uri);
+ soup_session_send_message (session, msg);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_object_unref (msg);
+ soup_session_abort (session);
+}
+
+static void
+do_connection_state_test_for_session (SoupSession *session)
+{
+ SoupConnectionState state;
+ SoupURI *proxy_uri;
+
+ g_signal_connect (session, "connection-created",
+ G_CALLBACK (connection_created),
+ &state);
+
+ debug_printf (1, " http\n");
+ do_one_connection_state_test (session, HTTP_SERVER);
+
+ if (tls_available) {
+ debug_printf (1, " https\n");
+ do_one_connection_state_test (session, HTTPS_SERVER);
+ } else
+ debug_printf (1, " https -- SKIPPING\n");
+
+ proxy_uri = soup_uri_new (HTTP_PROXY);
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ NULL);
+ soup_uri_free (proxy_uri);
+
+ debug_printf (1, " http with proxy\n");
+ do_one_connection_state_test (session, HTTP_SERVER);
+
+ if (tls_available) {
+ debug_printf (1, " https with proxy\n");
+ do_one_connection_state_test (session, HTTPS_SERVER);
+ } else
+ debug_printf (1, " https with proxy -- SKIPPING\n");
+}
+
+static void
+do_connection_state_test (void)
+{
+ SoupSession *session;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ debug_printf (1, " Async session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_connection_state_test_for_session (session);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ do_connection_state_test_for_session (session);
+ soup_test_session_abort_unref (session);
+}
+
+
+static const char *event_names[] = {
+ "RESOLVING", "RESOLVED", "CONNECTING", "CONNECTED",
+ "PROXY_NEGOTIATING", "PROXY_NEGOTIATED",
+ "TLS_HANDSHAKING", "TLS_HANDSHAKED", "COMPLETE"
+};
+
+static const char event_abbrevs[] = {
+ 'r', 'R', 'c', 'C', 'p', 'P', 't', 'T', 'x', '\0'
+};
+
+static const char *
+event_name_from_abbrev (char abbrev)
+{
+ int evt;
+
+ for (evt = 0; event_abbrevs[evt]; evt++) {
+ if (event_abbrevs[evt] == abbrev)
+ return event_names[evt];
+ }
+ return "???";
+}
+
+static void
+network_event (SoupMessage *msg, GSocketClientEvent event,
+ GIOStream *connection, gpointer user_data)
+{
+ const char **events = user_data;
+
+ debug_printf (2, " %s\n", event_name_from_abbrev (**events));
+ soup_test_assert (**events == event_abbrevs[event],
+ "Unexpected event: %s (expected %s)\n",
+ event_names[event],
+ event_name_from_abbrev (**events));
+ *events = *events + 1;
+}
+
+static void
+do_one_connection_event_test (SoupSession *session, const char *uri,
+ const char *events)
+{
+ SoupMessage *msg;
+
+ msg = soup_message_new ("GET", uri);
+ g_signal_connect (msg, "network-event",
+ G_CALLBACK (network_event),
+ &events);
+ soup_session_send_message (session, msg);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ while (*events) {
+ soup_test_assert (!*events,
+ "Expected %s",
+ event_name_from_abbrev (*events));
+ events++;
+ }
+
+ g_object_unref (msg);
+ soup_session_abort (session);
+}
+
+static void
+do_connection_event_test_for_session (SoupSession *session)
+{
+ SoupURI *proxy_uri;
+
+ debug_printf (1, " http\n");
+ do_one_connection_event_test (session, HTTP_SERVER, "rRcCx");
+
+ if (tls_available) {
+ debug_printf (1, " https\n");
+ do_one_connection_event_test (session, HTTPS_SERVER, "rRcCtTx");
+ } else
+ debug_printf (1, " https -- SKIPPING\n");
+
+ proxy_uri = soup_uri_new (HTTP_PROXY);
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ NULL);
+ soup_uri_free (proxy_uri);
+
+ debug_printf (1, " http with proxy\n");
+ do_one_connection_event_test (session, HTTP_SERVER, "rRcCx");
+
+ if (tls_available) {
+ debug_printf (1, " https with proxy\n");
+ do_one_connection_event_test (session, HTTPS_SERVER, "rRcCpPtTx");
+ } else
+ debug_printf (1, " https with proxy -- SKIPPING\n");
+}
+
+static void
+do_connection_event_test (void)
+{
+ SoupSession *session;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ debug_printf (1, " Async session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_connection_event_test_for_session (session);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ do_connection_event_test_for_session (session);
+ soup_test_session_abort_unref (session);
+}
+
int
main (int argc, char **argv)
{
+ int ret;
+
test_init (argc, argv, NULL);
+ apache_init ();
server = soup_test_server_new (TRUE);
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));
- do_content_length_framing_test ();
- do_persistent_connection_timeout_test ();
- do_max_conns_test ();
- do_non_persistent_connection_test ();
- do_non_idempotent_connection_test ();
+ 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);
+ g_test_add_func ("/connection/max-conns", do_max_conns_test);
+ g_test_add_func ("/connection/non-persistent", do_non_persistent_connection_test);
+ 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);
+
+ ret = g_test_run ();
soup_uri_free (base_uri);
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/context-test.c b/tests/context-test.c
index 97cd2c0c..727c63ba 100644
--- a/tests/context-test.c
+++ b/tests/context-test.c
@@ -84,13 +84,9 @@ static GMutex test1_mutex;
static GMainLoop *test1_loop;
static void
-do_test1 (int n, gboolean use_thread_context)
+do_test1 (gconstpointer data)
{
- debug_printf (1, "\nTest %d: blocking the main thread does not block other thread\n", n);
- if (use_thread_context)
- debug_printf (1, "(Using g_main_context_push_thread_default())\n");
- else
- debug_printf (1, "(Using SOUP_SESSION_ASYNC_CONTEXT)\n");
+ gboolean use_thread_context = GPOINTER_TO_INT (data);
test1_loop = g_main_loop_new (NULL, FALSE);
g_idle_add (idle_start_test1_thread, GINT_TO_POINTER (use_thread_context));
@@ -111,9 +107,8 @@ idle_start_test1_thread (gpointer use_thread_context)
if (g_cond_wait_until (&test1_cond, &test1_mutex, time))
g_thread_join (thread);
else {
- debug_printf (1, " timeout!\n");
+ soup_test_assert (FALSE, "timeout");
g_thread_unref (thread);
- errors++;
}
g_mutex_unlock (&test1_mutex);
@@ -158,11 +153,7 @@ test1_thread (gpointer use_thread_context)
debug_printf (1, " send_message\n");
msg = soup_message_new ("GET", uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " unexpected status: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_object_unref (msg);
debug_printf (1, " queue_message\n");
@@ -172,11 +163,7 @@ test1_thread (gpointer use_thread_context)
soup_session_queue_message (session, msg, test1_finished, loop);
g_main_loop_run (loop);
g_main_loop_unref (loop);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " unexpected status: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_object_unref (msg);
soup_test_session_abort_unref (session);
@@ -196,20 +183,15 @@ test1_thread (gpointer use_thread_context)
static gboolean idle_test2_fail (gpointer user_data);
static void
-do_test2 (int n, gboolean use_thread_context)
+do_test2 (gconstpointer data)
{
+ gboolean use_thread_context = GPOINTER_TO_INT (data);
guint idle;
GMainContext *async_context;
SoupSession *session;
char *uri;
SoupMessage *msg;
- debug_printf (1, "\nTest %d: a session with its own context is independent of the main loop.\n", n);
- if (use_thread_context)
- debug_printf (1, "(Using g_main_context_push_thread_default())\n");
- else
- debug_printf (1, "(Using SOUP_SESSION_ASYNC_CONTEXT)\n");
-
idle = g_idle_add_full (G_PRIORITY_HIGH, idle_test2_fail, NULL, NULL);
async_context = g_main_context_new ();
@@ -230,11 +212,7 @@ do_test2 (int n, gboolean use_thread_context)
debug_printf (1, " send_message\n");
msg = soup_message_new ("GET", uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " unexpected status: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_object_unref (msg);
soup_test_session_abort_unref (session);
@@ -249,8 +227,7 @@ do_test2 (int n, gboolean use_thread_context)
static gboolean
idle_test2_fail (gpointer user_data)
{
- debug_printf (1, " idle ran!\n");
- errors++;
+ soup_test_assert (FALSE, "idle ran");
return FALSE;
}
@@ -279,15 +256,13 @@ multi_msg_finished (SoupSession *session, SoupMessage *msg, gpointer user_data)
}
static void
-do_multicontext_test (int n)
+do_multicontext_test (void)
{
SoupSession *session;
SoupMessage *msg1, *msg2;
GMainContext *context1, *context2;
GMainLoop *loop1, *loop2;
- debug_printf (1, "\nTest %d: Using multiple async contexts\n", n);
-
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
@@ -318,36 +293,26 @@ do_multicontext_test (int n)
g_main_loop_run (loop1);
g_main_context_pop_thread_default (context1);
- if (!g_object_get_data (G_OBJECT (msg1), "started")) {
- debug_printf (1, " msg1 not started??\n");
- errors++;
- }
- if (g_object_get_data (G_OBJECT (msg2), "started")) {
- debug_printf (1, " msg2 started while loop1 was running!\n");
- errors++;
- }
+ if (!g_object_get_data (G_OBJECT (msg1), "started"))
+ soup_test_assert (FALSE, "msg1 not started");
+ if (g_object_get_data (G_OBJECT (msg2), "started"))
+ soup_test_assert (FALSE, "msg2 started while loop1 was running");
g_main_context_push_thread_default (context2);
g_main_loop_run (loop2);
g_main_context_pop_thread_default (context2);
- if (g_object_get_data (G_OBJECT (msg1), "finished")) {
- debug_printf (1, " msg1 finished while loop2 was running!\n");
- errors++;
- }
- if (!g_object_get_data (G_OBJECT (msg2), "finished")) {
- debug_printf (1, " msg2 not finished??\n");
- errors++;
- }
+ if (g_object_get_data (G_OBJECT (msg1), "finished"))
+ soup_test_assert (FALSE, "msg1 finished while loop2 was running");
+ if (!g_object_get_data (G_OBJECT (msg2), "finished"))
+ soup_test_assert (FALSE, "msg2 not finished");
g_main_context_push_thread_default (context1);
g_main_loop_run (loop1);
g_main_context_pop_thread_default (context1);
- if (!g_object_get_data (G_OBJECT (msg1), "finished")) {
- debug_printf (1, " msg1 not finished??\n");
- errors++;
- }
+ if (!g_object_get_data (G_OBJECT (msg1), "finished"))
+ soup_test_assert (FALSE, "msg1 not finished");
g_object_unref (msg1);
g_object_unref (msg2);
@@ -364,6 +329,7 @@ int
main (int argc, char **argv)
{
SoupServer *server;
+ int ret;
test_init (argc, argv, NULL);
@@ -372,15 +338,17 @@ main (int argc, char **argv)
base_uri = g_strdup_printf ("http://127.0.0.1:%u/",
soup_server_get_port (server));
- do_test1 (1, FALSE);
- do_test1 (2, TRUE);
- do_test2 (3, FALSE);
- do_test2 (4, TRUE);
- do_multicontext_test (5);
+ 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);
+ g_test_add_data_func ("/context/nested/explicit", GINT_TO_POINTER (FALSE), do_test2);
+ g_test_add_data_func ("/context/nested/thread-default", GINT_TO_POINTER (TRUE), do_test2);
+ g_test_add_func ("/context/multiple", do_multicontext_test);
+
+ ret = g_test_run ();
g_free (base_uri);
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/continue-test.c b/tests/continue-test.c
index fa9ef809..b6a5805f 100644
--- a/tests/continue-test.c
+++ b/tests/continue-test.c
@@ -59,13 +59,6 @@ do_message (const char *path, gboolean long_body,
const char *expected_event;
char *actual_event;
int expected_status, actual_status;
- static int count = 1;
-
- debug_printf (1, "%d. /%s, %s body, %sExpect, %s password\n",
- count++, path,
- long_body ? "long" : "short",
- expect_continue ? "" : "no ",
- auth ? "with" : "without");
uri = g_strdup_printf ("http://%s127.0.0.1:%d/%s",
auth ? "user:pass@" : "",
@@ -106,17 +99,13 @@ do_message (const char *path, gboolean long_body,
while ((expected_event = va_arg (ap, const char *))) {
if (!events) {
- actual_event = g_strdup ("");
- debug_printf (1, " Expected '%s', got end of list\n",
- expected_event);
- errors++;
+ soup_test_assert (events != NULL,
+ "Expected '%s', got end of list",
+ expected_event);
+ continue;
} else {
actual_event = events->data;
- if (strcmp (expected_event, actual_event) != 0) {
- debug_printf (1, " Expected '%s', got '%s'\n",
- expected_event, actual_event);
- errors++;
- }
+ g_assert_cmpstr (expected_event, ==, actual_event);
events = g_slist_delete_link (events, events);
}
@@ -134,10 +123,10 @@ do_message (const char *path, gboolean long_body,
if (expected_status != -1 && actual_status != -1 &&
expected_status != actual_status) {
- debug_printf (1, " Expected status '%s', got '%s'\n",
- soup_status_get_phrase (expected_status),
- soup_status_get_phrase (actual_status));
- errors++;
+ soup_test_assert (expected_status == actual_status,
+ "Expected status '%s', got '%s'",
+ soup_status_get_phrase (expected_status),
+ soup_status_get_phrase (actual_status));
}
g_free (actual_event);
@@ -145,8 +134,8 @@ do_message (const char *path, gboolean long_body,
va_end (ap);
while (events) {
actual_event = events->data;
- debug_printf (1, " Expected to be done, got '%s'\n", actual_event);
- errors++;
+ soup_test_assert (events == NULL,
+ "Expected to be done, got '%s'", actual_event);
events = g_slist_delete_link (events, events);
if (!strcmp (actual_event, "server-wrote_headers") ||
@@ -157,7 +146,7 @@ do_message (const char *path, gboolean long_body,
}
static void
-run_tests (void)
+do_test_unauth_short_noexpect_nopass (void)
{
do_message ("unauth", FALSE, FALSE, FALSE,
"client-wrote_headers",
@@ -171,6 +160,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_unauth_long_noexpect_nopass (void)
+{
do_message ("unauth", TRUE, FALSE, FALSE,
"client-wrote_headers",
"client-wrote_body",
@@ -183,6 +177,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_unauth_short_expect_nopass (void)
+{
do_message ("unauth", FALSE, TRUE, FALSE,
"client-wrote_headers",
"server-got_headers",
@@ -197,6 +196,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_unauth_long_expect_nopass (void)
+{
do_message ("unauth", TRUE, TRUE, FALSE,
"client-wrote_headers",
"server-got_headers",
@@ -207,7 +211,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+static void
+do_test_auth_short_noexpect_nopass (void)
+{
do_message ("auth", FALSE, FALSE, FALSE,
"client-wrote_headers",
"client-wrote_body",
@@ -220,6 +228,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_auth_long_noexpect_nopass (void)
+{
do_message ("auth", TRUE, FALSE, FALSE,
"client-wrote_headers",
"client-wrote_body",
@@ -232,6 +245,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_auth_short_expect_nopass (void)
+{
do_message ("auth", FALSE, TRUE, FALSE,
"client-wrote_headers",
"server-got_headers",
@@ -242,6 +260,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_auth_long_expect_nopass (void)
+{
do_message ("auth", TRUE, TRUE, FALSE,
"client-wrote_headers",
"server-got_headers",
@@ -252,7 +275,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+static void
+do_test_auth_short_noexpect_pass (void)
+{
do_message ("auth", FALSE, FALSE, TRUE,
"client-wrote_headers",
"client-wrote_body",
@@ -274,6 +301,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_auth_long_noexpect_pass (void)
+{
do_message ("auth", TRUE, FALSE, TRUE,
"client-wrote_headers",
"client-wrote_body",
@@ -295,6 +327,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_auth_short_expect_pass (void)
+{
do_message ("auth", FALSE, TRUE, TRUE,
"client-wrote_headers",
"server-got_headers",
@@ -316,6 +353,11 @@ run_tests (void)
"client-got_body",
"client-finished",
NULL);
+}
+
+static void
+do_test_auth_long_expect_pass (void)
+{
do_message ("auth", TRUE, TRUE, TRUE,
"client-wrote_headers",
"server-got_headers",
@@ -434,15 +476,30 @@ int
main (int argc, char **argv)
{
SoupServer *server;
+ int ret;
test_init (argc, argv, NULL);
server = setup_server ();
port = soup_server_get_port (server);
- run_tests ();
+ 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);
+ g_test_add_func ("/continue/unauth_short_expect_nopass", do_test_unauth_short_expect_nopass);
+ g_test_add_func ("/continue/unauth_long_expect_nopass", do_test_unauth_long_expect_nopass);
+ g_test_add_func ("/continue/auth_short_noexpect_nopass", do_test_auth_short_noexpect_nopass);
+ g_test_add_func ("/continue/auth_long_noexpect_nopass", do_test_auth_long_noexpect_nopass);
+ g_test_add_func ("/continue/auth_short_expect_nopass", do_test_auth_short_expect_nopass);
+ g_test_add_func ("/continue/auth_long_expect_nopass", do_test_auth_long_expect_nopass);
+ g_test_add_func ("/continue/auth_short_noexpect_pass", do_test_auth_short_noexpect_pass);
+ g_test_add_func ("/continue/auth_long_noexpect_pass", do_test_auth_long_noexpect_pass);
+ g_test_add_func ("/continue/auth_short_expect_pass", do_test_auth_short_expect_pass);
+ g_test_add_func ("/continue/auth_long_expect_pass", do_test_auth_long_expect_pass);
+
+ ret = g_test_run ();
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+
+ return ret;
}
diff --git a/tests/cookies-test.c b/tests/cookies-test.c
index 58f8052e..12529d81 100644
--- a/tests/cookies-test.c
+++ b/tests/cookies-test.c
@@ -55,8 +55,6 @@ do_cookies_accept_policy_test (void)
GSList *l, *p;
int i;
- debug_printf (1, "SoupCookieJarAcceptPolicy test\n");
-
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));
@@ -83,13 +81,7 @@ do_cookies_accept_policy_test (void)
g_object_unref (msg);
l = soup_cookie_jar_all_cookies (jar);
- if (g_slist_length (l) < validResults[i].n_cookies) {
- debug_printf (1, " accepted less cookies than it should have\n");
- errors++;
- } else if (g_slist_length (l) > validResults[i].n_cookies) {
- debug_printf (1, " accepted more cookies than it should have\n");
- errors++;
- }
+ g_assert_cmpint (g_slist_length (l), ==, validResults[i].n_cookies);
for (p = l; p; p = p->next) {
soup_cookie_jar_delete_cookie (jar, p->data);
@@ -113,7 +105,7 @@ do_cookies_parsing_test (void)
SoupCookie *cookie;
gboolean got1, got2, got3;
- debug_printf (1, "\nSoupCookie parsing test\n");
+ g_test_bug ("678753");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
@@ -146,56 +138,28 @@ do_cookies_parsing_test (void)
if (!strcmp (soup_cookie_get_name (cookie), "one")) {
got1 = TRUE;
- if (!soup_cookie_get_http_only (cookie)) {
- debug_printf (1, " cookie 1 is not HttpOnly!\n");
- errors++;
- }
- if (!soup_cookie_get_expires (cookie)) {
- debug_printf (1, " cookie 1 did not fully parse!\n");
- errors++;
- }
+ g_assert_true (soup_cookie_get_http_only (cookie));
+ g_assert_true (soup_cookie_get_expires (cookie) != NULL);
} else if (!strcmp (soup_cookie_get_name (cookie), "two")) {
got2 = TRUE;
- if (!soup_cookie_get_http_only (cookie)) {
- debug_printf (1, " cookie 2 is not HttpOnly!\n");
- errors++;
- }
- if (!soup_cookie_get_expires (cookie)) {
- debug_printf (1, " cookie 3 did not fully parse!\n");
- errors++;
- }
+ g_assert_true (soup_cookie_get_http_only (cookie));
+ g_assert_true (soup_cookie_get_expires (cookie) != NULL);
} else if (!strcmp (soup_cookie_get_name (cookie), "three")) {
got3 = TRUE;
- if (!soup_cookie_get_http_only (cookie)) {
- debug_printf (1, " cookie 3 is not HttpOnly!\n");
- errors++;
- }
- if (!soup_cookie_get_expires (cookie)) {
- debug_printf (1, " cookie 3 did not fully parse!\n");
- errors++;
- }
+ g_assert_true (soup_cookie_get_http_only (cookie));
+ g_assert_true (soup_cookie_get_expires (cookie) != NULL);
} else {
- debug_printf (1, " got unexpected cookie '%s'\n",
- soup_cookie_get_name (cookie));
- errors++;
+ soup_test_assert (FALSE, "got unexpected cookie '%s'",
+ soup_cookie_get_name (cookie));
}
soup_cookie_free (cookie);
}
g_slist_free (cookies);
- if (!got1) {
- debug_printf (1, " didn't get cookie 1\n");
- errors++;
- }
- if (!got2) {
- debug_printf (1, " didn't get cookie 2\n");
- errors++;
- }
- if (!got3) {
- debug_printf (1, " didn't get cookie 3\n");
- errors++;
- }
+ g_assert_true (got1);
+ g_assert_true (got2);
+ g_assert_true (got3);
soup_test_session_abort_unref (session);
}
@@ -203,6 +167,8 @@ do_cookies_parsing_test (void)
int
main (int argc, char **argv)
{
+ int ret;
+
test_init (argc, argv, NULL);
server = soup_test_server_new (TRUE);
@@ -212,14 +178,15 @@ main (int argc, char **argv)
soup_uri_set_port (first_party_uri, soup_server_get_port (server));
soup_uri_set_port (third_party_uri, soup_server_get_port (server));
- do_cookies_accept_policy_test ();
- do_cookies_parsing_test ();
+ g_test_add_func ("/cookies/accept-policy", do_cookies_accept_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_test_server_quit_unref (server);
test_cleanup ();
-
- return errors != 0;
+ return ret;
}
diff --git a/tests/date.c b/tests/date.c
index 54a59f2b..e51fa57f 100644
--- a/tests/date.c
+++ b/tests/date.c
@@ -5,7 +5,7 @@
#include "test-utils.h"
-static gboolean check_ok (const char *strdate, SoupDate *date);
+static void check_ok (gconstpointer data);
static SoupDate *
make_date (const char *strdate)
@@ -24,178 +24,226 @@ make_date (const char *strdate)
return date;
}
-static const struct {
+static SoupDate *
+check_correct_date (const char *strdate)
+{
+ SoupDate *date;
+
+ date = make_date (strdate);
+ if (!date) {
+ g_assert_nonnull (date);
+ return NULL;
+ }
+
+ g_assert_cmpint (date->year, ==, 2004);
+ g_assert_cmpint (date->month, ==, 11);
+ g_assert_cmpint (date->day, ==, 6);
+ g_assert_cmpint (date->hour, ==, 8);
+ g_assert_cmpint (date->minute, ==, 9);
+ g_assert_cmpint (date->second, ==, 7);
+
+ return date;
+}
+
+typedef struct {
SoupDateFormat format;
const char *date;
-} good_dates[] = {
- { SOUP_DATE_HTTP, "Sat, 06 Nov 2004 08:09:07 GMT" },
- { SOUP_DATE_COOKIE, "Sat, 06-Nov-2004 08:09:07 GMT" },
- { SOUP_DATE_RFC2822, "Sat, 6 Nov 2004 08:09:07 -0430" },
- { SOUP_DATE_ISO8601_COMPACT, "20041106T080907" },
- { SOUP_DATE_ISO8601_FULL, "2004-11-06T08:09:07" },
- { SOUP_DATE_ISO8601_XMLRPC, "20041106T08:09:07" }
+ const char *bugref;
+} GoodDate;
+
+static const GoodDate good_dates[] = {
+ { SOUP_DATE_HTTP, "Sat, 06 Nov 2004 08:09:07 GMT", NULL },
+ { SOUP_DATE_COOKIE, "Sat, 06-Nov-2004 08:09:07 GMT", NULL },
+ { SOUP_DATE_RFC2822, "Sat, 6 Nov 2004 08:09:07 -0430", "579055" },
+ { SOUP_DATE_ISO8601_COMPACT, "20041106T080907", NULL },
+ { SOUP_DATE_ISO8601_FULL, "2004-11-06T08:09:07", NULL },
+ { SOUP_DATE_ISO8601_XMLRPC, "20041106T08:09:07", NULL }
};
static void
-check_good (SoupDateFormat format, const char *strdate)
+check_good (gconstpointer data)
{
+ GoodDate *good = (GoodDate *)data;
SoupDate *date;
char *strdate2;
- date = make_date (strdate);
- g_assert (date);
- strdate2 = soup_date_to_string (date, format);
- if (!check_ok (strdate, date))
+ if (good->bugref)
+ g_test_bug (good->bugref);
+
+ date = check_correct_date (good->date);
+ if (!date)
return;
- if (strcmp (strdate, strdate2) != 0) {
- debug_printf (1, " restringification failed: '%s' -> '%s'\n",
- strdate, strdate2);
- errors++;
- }
+ strdate2 = soup_date_to_string (date, good->format);
+ soup_date_free (date);
+
+ soup_test_assert (strcmp (good->date, strdate2) == 0,
+ "restringification failed: '%s' -> '%s'\n",
+ good->date, strdate2);
g_free (strdate2);
}
-static const char *ok_dates[] = {
+typedef struct {
+ const char *date;
+ const char *bugref;
+} OkDate;
+
+static const OkDate ok_dates[] = {
/* rfc1123-date, and broken variants */
- "Sat, 06 Nov 2004 08:09:07 GMT",
- "Sat, 6 Nov 2004 08:09:07 GMT",
- "Sat, 6 Nov 2004 08:09:07 GMT",
- "Sat, 06 Nov 2004 08:09:07",
- "06 Nov 2004 08:09:07 GMT",
- "SAT, 06 NOV 2004 08:09:07 +1000",
+ { "Sat, 06 Nov 2004 08:09:07 GMT", NULL },
+ { "Sat, 6 Nov 2004 08:09:07 GMT", NULL },
+ { "Sat, 6 Nov 2004 08:09:07 GMT", NULL },
+ { "Sat, 06 Nov 2004 08:09:07", NULL },
+ { "06 Nov 2004 08:09:07 GMT", NULL },
+ { "SAT, 06 NOV 2004 08:09:07 +1000", "644048" },
/* rfc850-date, and broken variants */
- "Saturday, 06-Nov-04 08:09:07 GMT",
- "Saturday, 6-Nov-04 08:09:07 GMT",
- "Saturday, 6-Nov-04 08:09:07 GMT",
- "Saturday, 06-Nov-104 08:09:07 GMT",
- "Saturday, 06-Nov-04 08:09:07",
- "06-Nov-04 08:09:07 GMT",
+ { "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-04 08:09:07", NULL },
+ { "06-Nov-04 08:09:07 GMT", NULL },
/* asctime-date, and broken variants */
- "Sat Nov 6 08:09:07 2004",
- "Sat Nov 06 08:09:07 2004",
- "Sat Nov 6 08:09:07 2004",
- "Sat Nov 6 08:09:07 2004 GMT",
+ { "Sat Nov 6 08:09:07 2004", NULL },
+ { "Sat Nov 06 08:09:07 2004", NULL },
+ { "Sat Nov 6 08:09:07 2004", NULL },
+ { "Sat Nov 6 08:09:07 2004 GMT", NULL },
/* ISO 8601 */
- "2004-11-06T08:09:07Z",
- "20041106T08:09:07Z",
- "20041106T08:09:07+00:00",
- "20041106T080907+00:00",
+ { "2004-11-06T08:09:07Z", NULL },
+ { "20041106T08:09:07Z", NULL },
+ { "20041106T08:09:07+00:00", NULL },
+ { "20041106T080907+00:00", NULL },
/* Netscape cookie spec date, and broken variants */
- "Sat, 06-Nov-2004 08:09:07 GMT",
- "Sat, 6-Nov-2004 08:09:07 GMT",
- "Sat, 6-Nov-2004 08:09:07 GMT",
- "Sat, 06-Nov-2004 08:09:07",
+ { "Sat, 06-Nov-2004 08:09:07 GMT", NULL },
+ { "Sat, 6-Nov-2004 08:09:07 GMT", NULL },
+ { "Sat, 6-Nov-2004 08:09:07 GMT", NULL },
+ { "Sat, 06-Nov-2004 08:09:07", NULL },
/* Original version of Netscape cookie spec, and broken variants */
- "Sat, 06-Nov-04 08:09:07 GMT",
- "Sat, 6-Nov-04 08:09:07 GMT",
- "Sat, 6-Nov-04 08:09:07 GMT",
- "Sat, 06-Nov-104 08:09:07 GMT",
- "Sat, 06-Nov-04 08:09:07",
+ { "Sat, 06-Nov-04 08:09:07 GMT", NULL },
+ { "Sat, 6-Nov-04 08:09:07 GMT", NULL },
+ { "Sat, 6-Nov-04 08:09:07 GMT", NULL },
+ { "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",
- "Saturday, 6-Nov-04 08:09:07 GMT",
- "Saturday, 6-Nov-04 08:09:07 GMT",
- "Saturday, 06-Nov-104 08:09:07 GMT",
- "Saturday, 06-Nov-2004 08:09:07 GMT",
- "Saturday, 6-Nov-2004 08:09:07 GMT",
- "Saturday, 6-Nov-2004 08:09:07 GMT",
- "Saturday, 06-Nov-04 08:09:07",
+ { "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",
- "Saturday, 06-Nov-04 8:9:07 GMT",
- "Sat, 06 Nov 2004 08:09:7 GMT"
+ { "Sat 06-Nov-2004 08:9:07", NULL },
+ { "Saturday, 06-Nov-04 8:9:07 GMT", NULL },
+ { "Sat, 06 Nov 2004 08:09:7 GMT", NULL }
};
+static void
+check_ok (gconstpointer data)
+{
+ OkDate *ok = (OkDate *)data;
+ SoupDate *date;
+
+ if (ok->bugref)
+ g_test_bug (ok->bugref);
+
+ date = check_correct_date (ok->date);
+ if (!date)
+ return;
+ soup_date_free (date);
+}
+
#define TIME_T 1099728547L
#define TIME_T_STRING "1099728547"
-static gboolean
-check_ok (const char *strdate, SoupDate *date)
+static void
+check_ok_time_t (void)
{
- debug_printf (2, "%s\n", strdate);
+ SoupDate *date;
- if (date &&
- date->year == 2004 && date->month == 11 && date->day == 6 &&
- date->hour == 8 && date->minute == 9 && date->second == 7) {
- soup_date_free (date);
- return TRUE;
- }
+ date = soup_date_new_from_time_t (TIME_T);
- debug_printf (1, " date parsing failed for '%s'.\n", strdate);
- if (date) {
- debug_printf (1, " got: %d %d %d - %d %d %d\n\n",
- date->year, date->month, date->day,
- date->hour, date->minute, date->second);
- soup_date_free (date);
- }
- errors++;
- return FALSE;
+ g_assert_cmpint (date->year, ==, 2004);
+ g_assert_cmpint (date->month, ==, 11);
+ g_assert_cmpint (date->day, ==, 6);
+ g_assert_cmpint (date->hour, ==, 8);
+ g_assert_cmpint (date->minute, ==, 9);
+ g_assert_cmpint (date->second, ==, 7);
}
-static const char *bad_dates[] = {
+typedef struct {
+ const char *date;
+ const char *bugref;
+} BadDate;
+
+static const BadDate bad_dates[] = {
/* broken rfc1123-date */
- ", 06 Nov 2004 08:09:07 GMT",
- "Sat, Nov 2004 08:09:07 GMT",
- "Sat, 06 2004 08:09:07 GMT",
- "Sat, 06 Nov 08:09:07 GMT",
- "Sat, 06 Nov 2004 :09:07 GMT",
- "Sat, 06 Nov 2004 09:07 GMT",
- "Sat, 06 Nov 2004 08::07 GMT",
- "Sat, 06 Nov 2004 08:09: GMT",
+ { ", 06 Nov 2004 08:09:07 GMT", NULL },
+ { "Sat, Nov 2004 08:09:07 GMT", NULL },
+ { "Sat, 06 2004 08:09:07 GMT", NULL },
+ { "Sat, 06 Nov 08:09:07 GMT", NULL },
+ { "Sat, 06 Nov 2004 :09:07 GMT", NULL },
+ { "Sat, 06 Nov 2004 09:07 GMT", NULL },
+ { "Sat, 06 Nov 2004 08::07 GMT", NULL },
+ { "Sat, 06 Nov 2004 08:09: GMT", NULL },
/* broken rfc850-date */
- ", 06-Nov-04 08:09:07 GMT",
- "Saturday, -Nov-04 08:09:07 GMT",
- "Saturday, Nov-04 08:09:07 GMT",
- "Saturday, 06-04 08:09:07 GMT",
- "Saturday, 06--04 08:09:07 GMT",
- "Saturday, 06-Nov- 08:09:07 GMT",
- "Saturday, 06-Nov 08:09:07 GMT",
- "Saturday, 06-Nov-04 :09:07 GMT",
- "Saturday, 06-Nov-04 09:07 GMT",
- "Saturday, 06-Nov-04 08::07 GMT",
- "Saturday, 06-Nov-04 08:09: GMT",
+ { ", 06-Nov-04 08:09:07 GMT", NULL },
+ { "Saturday, -Nov-04 08:09:07 GMT", NULL },
+ { "Saturday, Nov-04 08:09:07 GMT", NULL },
+ { "Saturday, 06-04 08:09:07 GMT", NULL },
+ { "Saturday, 06--04 08:09:07 GMT", NULL },
+ { "Saturday, 06-Nov- 08:09:07 GMT", NULL },
+ { "Saturday, 06-Nov 08:09:07 GMT", NULL },
+ { "Saturday, 06-Nov-04 :09:07 GMT", NULL },
+ { "Saturday, 06-Nov-04 09:07 GMT", NULL },
+ { "Saturday, 06-Nov-04 08::07 GMT", NULL },
+ { "Saturday, 06-Nov-04 08:09: GMT", NULL },
/* broken asctime-date */
- "Nov 6 08:09:07 2004",
- "Sat 6 08:09:07 2004",
- "Sat Nov 08:09:07 2004",
- "Sat Nov 6 :09:07 2004",
- "Sat Nov 6 09:07 2004",
- "Sat Nov 6 08::07 2004",
- "Sat Nov 6 08:09: 2004",
- "Sat Nov 6 08:09:07",
- "Sat Nov 6 08:09:07 GMT 2004"
+ { "Nov 6 08:09:07 2004", NULL },
+ { "Sat 6 08:09:07 2004", NULL },
+ { "Sat Nov 08:09:07 2004", NULL },
+ { "Sat Nov 6 :09:07 2004", NULL },
+ { "Sat Nov 6 09:07 2004", NULL },
+ { "Sat Nov 6 08::07 2004", NULL },
+ { "Sat Nov 6 08:09: 2004", NULL },
+ { "Sat Nov 6 08:09:07", NULL },
+ { "Sat Nov 6 08:09:07 GMT 2004", NULL }
};
static void
-check_bad (const char *strdate, SoupDate *date)
+check_bad (gconstpointer data)
{
- debug_printf (2, "%s\n", strdate);
+ BadDate *bad = (BadDate *)data;
+ SoupDate *date;
- if (!date)
- return;
- errors++;
+ if (bad->bugref)
+ g_test_bug (bad->bugref);
- debug_printf (1, " date parsing succeeded for '%s'!\n", strdate);
- debug_printf (1, " got: %d %d %d - %d %d %d\n\n",
- date->year, date->month, date->day,
- date->hour, date->minute, date->second);
- soup_date_free (date);
+ date = make_date (bad->date);
+ soup_test_assert (date == NULL,
+ "date parsing succeeded for '%s': %d %d %d - %d %d %d",
+ bad->date,
+ date->year, date->month, date->day,
+ date->hour, date->minute, date->second);
+ g_clear_pointer (&date, soup_date_free);
}
-static const struct conversion {
+typedef struct {
const char *source;
const char *http, *cookie, *rfc2822, *compact, *full, *xmlrpc;
-} conversions[] = {
+} DateConversion;
+
+static const DateConversion conversions[] = {
/* SOUP_DATE_HTTP */
{ "Sat, 06 Nov 2004 08:09:07 GMT",
@@ -288,71 +336,40 @@ static const struct conversion {
};
static void
-check_conversion (const struct conversion *conv)
+check_conversion (gconstpointer data)
{
+ const DateConversion *conv = data;
SoupDate *date;
char *str;
- debug_printf (2, "%s\n", conv->source);
date = make_date (conv->source);
if (!date) {
- debug_printf (1, " date parsing failed for '%s'.\n", conv->source);
- errors++;
+ soup_test_assert (FALSE, "date parsing failed for '%s'.", conv->source);
return;
}
str = soup_date_to_string (date, SOUP_DATE_HTTP);
- if (!str || strcmp (str, conv->http) != 0) {
- debug_printf (1, " conversion of '%s' to HTTP failed:\n"
- " wanted: %s\n got: %s\n",
- conv->source, conv->http, str ? str : "(null)");
- errors++;
- }
+ g_assert_cmpstr (str, ==, conv->http);
g_free (str);
str = soup_date_to_string (date, SOUP_DATE_COOKIE);
- if (!str || strcmp (str, conv->cookie) != 0) {
- debug_printf (1, " conversion of '%s' to COOKIE failed:\n"
- " wanted: %s\n got: %s\n",
- conv->source, conv->cookie, str ? str : "(null)");
- errors++;
- }
+ g_assert_cmpstr (str, ==, conv->cookie);
g_free (str);
str = soup_date_to_string (date, SOUP_DATE_RFC2822);
- if (!str || strcmp (str, conv->rfc2822) != 0) {
- debug_printf (1, " conversion of '%s' to RFC2822 failed:\n"
- " wanted: %s\n got: %s\n",
- conv->source, conv->rfc2822, str ? str : "(null)");
- errors++;
- }
+ g_assert_cmpstr (str, ==, conv->rfc2822);
g_free (str);
str = soup_date_to_string (date, SOUP_DATE_ISO8601_COMPACT);
- if (!str || strcmp (str, conv->compact) != 0) {
- debug_printf (1, " conversion of '%s' to COMPACT failed:\n"
- " wanted: %s\n got: %s\n",
- conv->source, conv->compact, str ? str : "(null)");
- errors++;
- }
+ g_assert_cmpstr (str, ==, conv->compact);
g_free (str);
str = soup_date_to_string (date, SOUP_DATE_ISO8601_FULL);
- if (!str || strcmp (str, conv->full) != 0) {
- debug_printf (1, " conversion of '%s' to FULL failed:\n"
- " wanted: %s\n got: %s\n",
- conv->source, conv->full, str ? str : "(null)");
- errors++;
- }
+ g_assert_cmpstr (str, ==, conv->full);
g_free (str);
str = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
- if (!str || strcmp (str, conv->xmlrpc) != 0) {
- debug_printf (1, " conversion of '%s' to XMLRPC failed:\n"
- " wanted: %s\n got: %s\n",
- conv->source, conv->xmlrpc, str ? str : "(null)");
- errors++;
- }
+ g_assert_cmpstr (str, ==, conv->xmlrpc);
g_free (str);
soup_date_free (date);
@@ -361,27 +378,38 @@ check_conversion (const struct conversion *conv)
int
main (int argc, char **argv)
{
- int i;
+ int i, ret;
+ char *path;
test_init (argc, argv, NULL);
- debug_printf (1, "Good dates:\n");
- for (i = 0; i < G_N_ELEMENTS (good_dates); i++)
- check_good (good_dates[i].format, good_dates[i].date);
+ for (i = 0; i < G_N_ELEMENTS (good_dates); i++) {
+ path = g_strdup_printf ("/date/good/%s", good_dates[i].date);
+ g_test_add_data_func (path, &good_dates[i], check_good);
+ g_free (path);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (ok_dates); i++) {
+ path = g_strdup_printf ("/date/ok/%s", ok_dates[i].date);
+ g_test_add_data_func (path, &ok_dates[i], check_ok);
+ g_free (path);
+ }
+ g_test_add_func ("/date/ok/" TIME_T_STRING, check_ok_time_t);
- debug_printf (1, "\nOK dates:\n");
- for (i = 0; i < G_N_ELEMENTS (ok_dates); i++)
- check_ok (ok_dates[i], make_date (ok_dates[i]));
- check_ok (TIME_T_STRING, soup_date_new_from_time_t (TIME_T));
+ for (i = 0; i < G_N_ELEMENTS (bad_dates); i++) {
+ path = g_strdup_printf ("/date/bad/%s", bad_dates[i].date);
+ g_test_add_data_func (path, &bad_dates[i], check_bad);
+ g_free (path);
+ }
- debug_printf (1, "\nBad dates:\n");
- for (i = 0; i < G_N_ELEMENTS (bad_dates); i++)
- check_bad (bad_dates[i], make_date (bad_dates[i]));
+ for (i = 0; i < G_N_ELEMENTS (conversions); i++) {
+ path = g_strdup_printf ("/date/conversions/%s", conversions[i].source);
+ g_test_add_data_func (path, &conversions[i], check_conversion);
+ g_free (path);
+ }
- debug_printf (1, "\nConversions:\n");
- for (i = 0; i < G_N_ELEMENTS (conversions); i++)
- check_conversion (&conversions[i] );
+ ret = g_test_run ();
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/dns.c b/tests/dns.c
deleted file mode 100644
index f5940647..00000000
--- a/tests/dns.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-
-#include "test-utils.h"
-
-static GMainLoop *loop;
-static int nlookups = 0;
-
-static void
-resolve_callback (SoupAddress *addr, guint status, gpointer data)
-{
- if (status == SOUP_STATUS_OK) {
- g_print ("Name: %s\n", soup_address_get_name (addr));
- g_print ("Address: %s\n", soup_address_get_physical (addr));
- } else {
- g_print ("Name: %s\n", soup_address_get_name (addr));
- g_print ("Error: %s\n", soup_status_get_phrase (status));
- }
- g_print ("\n");
-
- g_object_unref (addr);
-
- nlookups--;
- if (nlookups == 0)
- g_main_loop_quit (loop);
-}
-
-static void
-usage (void)
-{
- g_printerr ("Usage: dns hostname ...\n");
- exit (1);
-}
-
-int
-main (int argc, char **argv)
-{
- SoupAddress *addr;
- int i;
-
- if (argc < 2)
- usage ();
-
- g_type_init ();
-
- for (i = 1; i < argc; i++) {
- addr = soup_address_new (argv[i], 0);
- if (!addr) {
- g_printerr ("Could not parse address %s\n", argv[1]);
- exit (1);
- }
-
- soup_address_resolve_async (addr, NULL, NULL,
- resolve_callback, NULL);
- nlookups++;
- }
-
- loop = g_main_loop_new (NULL, TRUE);
- g_main_loop_run (loop);
- g_main_loop_unref (loop);
-
- return 0;
-}
diff --git a/tests/forms-test.c b/tests/forms-test.c
index 1d254b12..3915b019 100644
--- a/tests/forms-test.c
+++ b/tests/forms-test.c
@@ -41,6 +41,7 @@ do_hello_test (int n, gboolean extra, const char *uri)
GPtrArray *args;
char *title_arg = NULL, *name_arg = NULL;
char *str_stdout = NULL;
+ GError *error = NULL;
debug_printf (1, "%2d. '%s' '%s'%s: ", n * 2 + (extra ? 2 : 1),
tests[n].title ? tests[n].title : "(null)",
@@ -49,6 +50,8 @@ do_hello_test (int n, gboolean extra, const char *uri)
args = g_ptr_array_new ();
g_ptr_array_add (args, "curl");
+ g_ptr_array_add (args, "--noproxy");
+ g_ptr_array_add (args, "*");
g_ptr_array_add (args, "-G");
if (tests[n].title) {
title_arg = soup_form_encode ("title", tests[n].title, NULL);
@@ -70,20 +73,12 @@ do_hello_test (int n, gboolean extra, const char *uri)
if (g_spawn_sync (NULL, (char **)args->pdata, NULL,
G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
NULL, NULL,
- &str_stdout, NULL, NULL, NULL)) {
- if (str_stdout && !strcmp (str_stdout, tests[n].result))
- debug_printf (1, "OK!\n");
- else {
- debug_printf (1, "WRONG!\n");
- debug_printf (1, " expected '%s', got '%s'\n",
- tests[n].result,
- str_stdout ? str_stdout : "(error)");
- errors++;
- }
+ &str_stdout, NULL, NULL, &error)) {
+ g_assert_cmpstr (str_stdout, ==, tests[n].result);
g_free (str_stdout);
} else {
- debug_printf (1, "ERROR!\n");
- errors++;
+ g_assert_no_error (error);
+ g_error_free (error);
}
g_ptr_array_free (args, TRUE);
g_free (title_arg);
@@ -91,30 +86,75 @@ do_hello_test (int n, gboolean extra, const char *uri)
}
static void
-do_hello_tests (const char *uri)
+do_hello_tests (gconstpointer uri)
{
int n;
- debug_printf (1, "Hello tests (GET, application/x-www-form-urlencoded)\n");
+#ifndef HAVE_CURL
+ g_test_skip ("/usr/bin/curl is not available");
+ return;
+#endif
+
for (n = 0; n < G_N_ELEMENTS (tests); n++) {
do_hello_test (n, FALSE, uri);
do_hello_test (n, TRUE, uri);
}
}
+#define MD5_TEST_FILE (g_test_get_filename (G_TEST_DIST, "index.txt", NULL))
+#define MD5_TEST_FILE_BASENAME "index.txt"
+#define MD5_TEST_FILE_MIME_TYPE "text/plain"
+
+static char *
+get_md5_data (char **contents, gsize *length)
+{
+ char *my_contents, *md5;
+ gsize my_length;
+ GError *error = NULL;
+
+ if (!g_file_get_contents (MD5_TEST_FILE, &my_contents, &my_length, &error)) {
+ g_assert_no_error (error);
+ g_error_free (error);
+ return NULL;
+ }
+
+ md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, my_contents, my_length);
+
+ if (contents)
+ *contents = my_contents;
+ else
+ g_free (my_contents);
+ if (length)
+ *length = my_length;
+
+ return md5;
+}
+
static void
-do_md5_test_curl (const char *uri, const char *file, const char *md5)
+do_md5_test_curl (gconstpointer data)
{
+ const char *uri = data;
+ char *md5;
GPtrArray *args;
char *file_arg, *str_stdout;
+ GError *error = NULL;
- debug_printf (1, " via curl: ");
+#ifndef HAVE_CURL
+ g_test_skip ("/usr/bin/curl is not available");
+ return;
+#endif
+
+ md5 = get_md5_data (NULL, NULL);
+ if (!md5)
+ return;
args = g_ptr_array_new ();
g_ptr_array_add (args, "curl");
+ g_ptr_array_add (args, "--noproxy");
+ g_ptr_array_add (args, "*");
g_ptr_array_add (args, "-L");
g_ptr_array_add (args, "-F");
- file_arg = g_strdup_printf ("file=@%s", file);
+ file_arg = g_strdup_printf ("file=@%s", MD5_TEST_FILE);
g_ptr_array_add (args, file_arg);
g_ptr_array_add (args, "-F");
g_ptr_array_add (args, "fmt=txt");
@@ -125,37 +165,34 @@ do_md5_test_curl (const char *uri, const char *file, const char *md5)
G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL,
NULL, NULL,
&str_stdout, NULL, NULL, NULL)) {
- if (str_stdout && !strcmp (str_stdout, md5))
- debug_printf (1, "OK!\n");
- else {
- debug_printf (1, "WRONG!\n");
- debug_printf (1, " expected '%s', got '%s'\n",
- md5, str_stdout ? str_stdout : "(error)");
- errors++;
- }
+ g_assert_cmpstr (str_stdout, ==, md5);
g_free (str_stdout);
} else {
- debug_printf (1, "ERROR!\n");
- errors++;
+ g_assert_no_error (error);
+ g_error_free (error);
}
g_ptr_array_free (args, TRUE);
g_free (file_arg);
-}
-#define MD5_TEST_FILE SRCDIR "/resources/home.gif"
-#define MD5_TEST_FILE_BASENAME "home.gif"
-#define MD5_TEST_FILE_MIME_TYPE "image/gif"
+ g_free (md5);
+}
static void
-do_md5_test_libsoup (const char *uri, const char *contents,
- gsize length, const char *md5)
+do_md5_test_libsoup (gconstpointer data)
{
+ const char *uri = data;
+ char *contents, *md5;
+ gsize length;
SoupMultipart *multipart;
SoupBuffer *buffer;
SoupMessage *msg;
SoupSession *session;
- debug_printf (1, " via libsoup: ");
+ g_test_bug ("601640");
+
+ md5 = get_md5_data (&contents, &length);
+ if (!md5)
+ return;
multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
buffer = soup_buffer_new (SOUP_MEMORY_COPY, contents, length);
@@ -172,47 +209,16 @@ do_md5_test_libsoup (const char *uri, const char *contents,
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, "ERROR: Unexpected status %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- } else if (strcmp (msg->response_body->data, md5) != 0) {
- debug_printf (1, "ERROR: Incorrect response: expected '%s' got '%s'\n",
- md5, msg->response_body->data);
- errors++;
- } else
- debug_printf (1, "OK!\n");
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_assert_cmpstr (msg->response_body->data, ==, md5);
g_object_unref (msg);
soup_test_session_abort_unref (session);
-}
-
-static void
-do_md5_tests (const char *uri)
-{
- char *contents, *md5;
- gsize length;
- GError *error = NULL;
-
- debug_printf (1, "\nMD5 tests (POST, multipart/form-data)\n");
-
- if (!g_file_get_contents (MD5_TEST_FILE, &contents, &length, &error)) {
- debug_printf (1, " ERROR: Could not read " MD5_TEST_FILE ": %s\n", error->message);
- g_error_free (error);
- errors++;
- return;
- }
-
- md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, contents, length);
-
- do_md5_test_curl (uri, MD5_TEST_FILE, md5);
- do_md5_test_libsoup (uri, contents, length, md5);
g_free (contents);
g_free (md5);
}
-
static void
do_form_decode_test (void)
{
@@ -220,7 +226,10 @@ do_form_decode_test (void)
const gchar *value;
gchar *tmp;
- debug_printf (1, "\nDecode tests\n");
+#ifndef HAVE_CURL
+ g_test_skip ("/usr/bin/curl is not available");
+ return;
+#endif
/* Test that the code handles multiple values with the same key. */
table = soup_form_decode ("foo=first&foo=second&foo=third");
@@ -232,11 +241,7 @@ do_form_decode_test (void)
tmp = g_strdup ("other");
value = g_hash_table_lookup (table, "foo");
- if (g_strcmp0 (value, "third") != 0) {
- debug_printf (1, " ERROR: expected '%s', got '%s'\n",
- "third", value ? value : "(null)");
- errors++;
- }
+ g_assert_cmpstr (value, ==, "third");
g_free (tmp);
g_hash_table_destroy (table);
@@ -417,6 +422,7 @@ main (int argc, char **argv)
SoupServer *server;
guint port;
char *uri_str;
+ int ret = 0;
test_init (argc, argv, no_test_entry);
@@ -431,14 +437,16 @@ main (int argc, char **argv)
if (run_tests) {
uri_str = g_strdup_printf ("http://127.0.0.1:%u/hello", port);
- do_hello_tests (uri_str);
- g_free (uri_str);
+ g_test_add_data_func_full ("/forms/hello", uri_str, do_hello_tests, g_free);
uri_str = g_strdup_printf ("http://127.0.0.1:%u/md5", port);
- do_md5_tests (uri_str);
+ 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);
- do_form_decode_test ();
+ g_test_add_func ("/forms/decode", do_form_decode_test);
+
+ ret = g_test_run ();
} else {
g_print ("Listening on port %d\n", port);
g_main_loop_run (loop);
@@ -449,5 +457,5 @@ main (int argc, char **argv)
soup_test_server_quit_unref (server);
if (run_tests)
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/get.c b/tests/get.c
deleted file mode 100644
index 1d867155..00000000
--- a/tests/get.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2001-2003, Ximian, Inc.
- */
-
-#include "test-utils.h"
-#include <stdio.h>
-
-static SoupSession *session;
-static GMainLoop *loop;
-static gboolean debug = FALSE, quiet = FALSE;
-static const char *method;
-
-static void
-get_url (const char *url)
-{
- const char *name;
- SoupMessage *msg;
- const char *header;
-
- msg = soup_message_new (method, url);
- soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
-
- soup_session_send_message (session, msg);
-
- name = soup_message_get_uri (msg)->path;
-
- if (debug) {
- SoupMessageHeadersIter iter;
- const char *hname, *value;
- char *path = soup_uri_to_string (soup_message_get_uri (msg), TRUE);
-
- g_print ("%s %s HTTP/1.%d\n", method, path,
- soup_message_get_http_version (msg));
- soup_message_headers_iter_init (&iter, msg->request_headers);
- while (soup_message_headers_iter_next (&iter, &hname, &value))
- g_print ("%s: %s\r\n", hname, value);
- g_print ("\n");
-
- g_print ("HTTP/1.%d %d %s\n",
- soup_message_get_http_version (msg),
- msg->status_code, msg->reason_phrase);
-
- soup_message_headers_iter_init (&iter, msg->response_headers);
- while (soup_message_headers_iter_next (&iter, &hname, &value))
- g_print ("%s: %s\r\n", hname, value);
- g_print ("\n");
- } else if (msg->status_code == SOUP_STATUS_SSL_FAILED) {
- GTlsCertificateFlags flags;
-
- if (soup_message_get_https_status (msg, NULL, &flags))
- g_print ("%s: %d %s (0x%x)\n", name, msg->status_code, msg->reason_phrase, flags);
- else
- g_print ("%s: %d %s (no handshake status)\n", name, msg->status_code, msg->reason_phrase);
- } else if (!quiet || SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
- g_print ("%s: %d %s\n", name, msg->status_code, msg->reason_phrase);
-
- if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
- header = soup_message_headers_get_one (msg->response_headers,
- "Location");
- if (header) {
- SoupURI *uri;
- char *uri_string;
-
- if (!debug && !quiet)
- g_print (" -> %s\n", header);
-
- uri = soup_uri_new_with_base (soup_message_get_uri (msg), header);
- uri_string = soup_uri_to_string (uri, FALSE);
- get_url (uri_string);
- g_free (uri_string);
- soup_uri_free (uri);
- }
- } else if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- fwrite (msg->response_body->data, 1,
- msg->response_body->length, stdout);
- }
-}
-
-static void
-usage (void)
-{
- g_printerr ("Usage: get [-c CAfile] [-p proxy URL] [-h] [-d] URL\n");
- exit (1);
-}
-
-int
-main (int argc, char **argv)
-{
- const char *cafile = NULL, *url;
- SoupURI *proxy = NULL, *parsed;
- gboolean synchronous = FALSE, ntlm = FALSE;
- int opt;
-
- g_type_init ();
-
- method = SOUP_METHOD_GET;
-
- while ((opt = getopt (argc, argv, "c:dhnp:qs")) != -1) {
- switch (opt) {
- case 'c':
- cafile = optarg;
- break;
-
- case 'd':
- debug = TRUE;
- break;
-
- case 'h':
- method = SOUP_METHOD_HEAD;
- debug = TRUE;
- break;
-
- case 'n':
- ntlm = TRUE;
- break;
-
- case 'p':
- proxy = soup_uri_new (optarg);
- if (!proxy) {
- g_printerr ("Could not parse %s as URI\n",
- optarg);
- exit (1);
- }
- break;
-
- case 'q':
- quiet = TRUE;
- break;
-
- case 's':
- synchronous = TRUE;
- break;
-
- case '?':
- usage ();
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc != 1)
- usage ();
- url = argv[0];
- parsed = soup_uri_new (url);
- if (!parsed) {
- g_printerr ("Could not parse '%s' as a URL\n", url);
- exit (1);
- }
- soup_uri_free (parsed);
-
- if (synchronous) {
- session = soup_session_sync_new_with_options (
- SOUP_SESSION_SSL_CA_FILE, cafile,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
- SOUP_SESSION_USER_AGENT, "get ",
- SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
- SOUP_SESSION_USE_NTLM, ntlm,
- NULL);
- } else {
- session = soup_session_async_new_with_options (
- SOUP_SESSION_SSL_CA_FILE, cafile,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
- SOUP_SESSION_USER_AGENT, "get ",
- SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
- SOUP_SESSION_USE_NTLM, ntlm,
- NULL);
- }
-
- if (proxy) {
- g_object_set (G_OBJECT (session),
- SOUP_SESSION_PROXY_URI, proxy,
- NULL);
- } else
- soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
-
- if (!synchronous)
- loop = g_main_loop_new (NULL, TRUE);
-
- get_url (url);
-
- if (!synchronous)
- g_main_loop_unref (loop);
-
- return 0;
-}
diff --git a/tests/header-parsing.c b/tests/header-parsing.c
index e57d7d83..fdc7885d 100644
--- a/tests/header-parsing.c
+++ b/tests/header-parsing.c
@@ -8,6 +8,7 @@ typedef struct {
static struct RequestTest {
const char *description;
+ const char *bugref;
const char *request;
int length;
guint status;
@@ -19,14 +20,14 @@ static struct RequestTest {
/*** VALID REQUESTS ***/
/**********************/
- { "HTTP 1.0 request with no headers",
+ { "HTTP 1.0 request with no headers", NULL,
"GET / HTTP/1.0\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_0,
{ { NULL } }
},
- { "Req w/ 1 header",
+ { "Req w/ 1 header", NULL,
"GET / HTTP/1.1\r\nHost: example.com\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -35,7 +36,7 @@ static struct RequestTest {
}
},
- { "Req w/ 1 header, no leading whitespace",
+ { "Req w/ 1 header, no leading whitespace", NULL,
"GET / HTTP/1.1\r\nHost:example.com\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -44,7 +45,7 @@ static struct RequestTest {
}
},
- { "Req w/ 1 header including trailing whitespace",
+ { "Req w/ 1 header including trailing whitespace", NULL,
"GET / HTTP/1.1\r\nHost: example.com \r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -53,7 +54,7 @@ static struct RequestTest {
}
},
- { "Req w/ 1 header, wrapped",
+ { "Req w/ 1 header, wrapped", NULL,
"GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -62,7 +63,7 @@ static struct RequestTest {
}
},
- { "Req w/ 1 header, wrapped with additional whitespace",
+ { "Req w/ 1 header, wrapped with additional whitespace", NULL,
"GET / HTTP/1.1\r\nFoo: bar \r\n baz\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -71,7 +72,7 @@ static struct RequestTest {
}
},
- { "Req w/ 1 header, wrapped with tab",
+ { "Req w/ 1 header, wrapped with tab", NULL,
"GET / HTTP/1.1\r\nFoo: bar\r\n\tbaz\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -80,7 +81,7 @@ static struct RequestTest {
}
},
- { "Req w/ 1 header, wrapped before value",
+ { "Req w/ 1 header, wrapped before value", NULL,
"GET / HTTP/1.1\r\nFoo:\r\n bar baz\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -89,7 +90,7 @@ static struct RequestTest {
}
},
- { "Req w/ 1 header with empty value",
+ { "Req w/ 1 header with empty value", NULL,
"GET / HTTP/1.1\r\nHost:\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -98,7 +99,7 @@ static struct RequestTest {
}
},
- { "Req w/ 2 headers",
+ { "Req w/ 2 headers", NULL,
"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -108,7 +109,7 @@ static struct RequestTest {
}
},
- { "Req w/ 3 headers",
+ { "Req w/ 3 headers", NULL,
"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\nBlah: blah\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -119,7 +120,7 @@ static struct RequestTest {
}
},
- { "Req w/ 3 headers, 1st wrapped",
+ { "Req w/ 3 headers, 1st wrapped", NULL,
"GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -130,7 +131,7 @@ static struct RequestTest {
}
},
- { "Req w/ 3 headers, 2nd wrapped",
+ { "Req w/ 3 headers, 2nd wrapped", NULL,
"GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -141,7 +142,7 @@ static struct RequestTest {
}
},
- { "Req w/ 3 headers, 3rd wrapped",
+ { "Req w/ 3 headers, 3rd wrapped", NULL,
"GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -152,7 +153,7 @@ static struct RequestTest {
}
},
- { "Req w/ same header multiple times",
+ { "Req w/ same header multiple times", NULL,
"GET / HTTP/1.1\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -161,7 +162,7 @@ static struct RequestTest {
}
},
- { "Connection header on HTTP/1.0 message",
+ { "Connection header on HTTP/1.0 message", NULL,
"GET / HTTP/1.0\r\nFoo: bar\r\nConnection: Bar, Quux\r\nBar: baz\r\nQuux: foo\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_0,
@@ -171,14 +172,14 @@ static struct RequestTest {
}
},
- { "GET with full URI",
+ { "GET with full URI", "667637",
"GET http://example.com HTTP/1.1\r\n", -1,
SOUP_STATUS_OK,
"GET", "http://example.com", SOUP_HTTP_1_1,
{ { NULL } }
},
- { "GET with full URI in upper-case",
+ { "GET with full URI in upper-case", "667637",
"GET HTTP://example.com HTTP/1.1\r\n", -1,
SOUP_STATUS_OK,
"GET", "HTTP://example.com", SOUP_HTTP_1_1,
@@ -188,7 +189,7 @@ static struct RequestTest {
/* It's better for this to be passed through: this means a SoupServer
* could implement ftp-over-http proxying, for instance
*/
- { "GET with full URI of unrecognised scheme",
+ { "GET with full URI of unrecognised scheme", "667637",
"GET AbOuT: HTTP/1.1\r\n", -1,
SOUP_STATUS_OK,
"GET", "AbOuT:", SOUP_HTTP_1_1,
@@ -201,7 +202,7 @@ static struct RequestTest {
/* RFC 2616 section 4.1 says we SHOULD accept this */
- { "Spurious leading CRLF",
+ { "Spurious leading CRLF", NULL,
"\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -212,7 +213,7 @@ static struct RequestTest {
/* RFC 2616 section 3.1 says we MUST accept this */
- { "HTTP/01.01 request",
+ { "HTTP/01.01 request", NULL,
"GET / HTTP/01.01\r\nHost: example.com\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -223,7 +224,7 @@ static struct RequestTest {
/* RFC 2616 section 19.3 says we SHOULD accept these */
- { "LF instead of CRLF after header",
+ { "LF instead of CRLF after header", NULL,
"GET / HTTP/1.1\r\nHost: example.com\nConnection: close\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -233,7 +234,7 @@ static struct RequestTest {
}
},
- { "LF instead of CRLF after Request-Line",
+ { "LF instead of CRLF after Request-Line", NULL,
"GET / HTTP/1.1\nHost: example.com\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -242,7 +243,7 @@ static struct RequestTest {
}
},
- { "Mixed CRLF/LF",
+ { "Mixed CRLF/LF", "666316",
"GET / HTTP/1.1\r\na: b\r\nc: d\ne: f\r\ng: h\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -254,7 +255,7 @@ static struct RequestTest {
}
},
- { "Req w/ incorrect whitespace in Request-Line",
+ { "Req w/ incorrect whitespace in Request-Line", NULL,
"GET /\tHTTP/1.1\r\nHost: example.com\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -263,7 +264,7 @@ static struct RequestTest {
}
},
- { "Req w/ incorrect whitespace after Request-Line",
+ { "Req w/ incorrect whitespace after Request-Line", "475169",
"GET / HTTP/1.1 \r\nHost: example.com\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -274,10 +275,9 @@ static struct RequestTest {
/* If the request/status line is parseable, then we
* just ignore any invalid-looking headers after that.
- * (qv bug 579318).
*/
- { "Req w/ mangled header",
+ { "Req w/ mangled header", "579318",
"GET / HTTP/1.1\r\nHost: example.com\r\nFoo one\r\nBar: two\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -287,7 +287,7 @@ static struct RequestTest {
}
},
- { "First header line is continuation",
+ { "First header line is continuation", "666316",
"GET / HTTP/1.1\r\n b\r\nHost: example.com\r\nc: d\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -297,7 +297,7 @@ static struct RequestTest {
}
},
- { "Zero-length header name",
+ { "Zero-length header name", "666316",
"GET / HTTP/1.1\r\na: b\r\n: example.com\r\nc: d\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -307,7 +307,7 @@ static struct RequestTest {
}
},
- { "CR in header name",
+ { "CR in header name", "666316",
"GET / HTTP/1.1\r\na: b\r\na\rb: cd\r\nx\r: y\r\n\rz: w\r\nc: d\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -317,7 +317,7 @@ static struct RequestTest {
}
},
- { "CR in header value",
+ { "CR in header value", "666316",
"GET / HTTP/1.1\r\na: b\r\nHost: example\rcom\r\np: \rq\r\ns: t\r\r\nc: d\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -330,7 +330,7 @@ static struct RequestTest {
}
},
- { "Tab in header name",
+ { "Tab in header name", "666316",
"GET / HTTP/1.1\r\na: b\r\na\tb: cd\r\nx\t: y\r\np: q\r\n\tz: w\r\nc: d\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -345,7 +345,7 @@ static struct RequestTest {
}
},
- { "Tab in header value",
+ { "Tab in header value", "666316",
"GET / HTTP/1.1\r\na: b\r\nab: c\td\r\nx: \ty\r\nz: w\t\r\nc: d\r\n", -1,
SOUP_STATUS_OK,
"GET", "/", SOUP_HTTP_1_1,
@@ -362,84 +362,84 @@ static struct RequestTest {
/*** INVALID REQUESTS ***/
/************************/
- { "HTTP 0.9 request; not supported",
+ { "HTTP 0.9 request; not supported", NULL,
"GET /\r\n", -1,
SOUP_STATUS_BAD_REQUEST,
NULL, NULL, -1,
{ { NULL } }
},
- { "HTTP 1.2 request (no such thing)",
+ { "HTTP 1.2 request (no such thing)", NULL,
"GET / HTTP/1.2\r\n", -1,
SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
NULL, NULL, -1,
{ { NULL } }
},
- { "HTTP 2000 request (no such thing)",
+ { "HTTP 2000 request (no such thing)", NULL,
"GET / HTTP/2000.0\r\n", -1,
SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
NULL, NULL, -1,
{ { NULL } }
},
- { "Non-HTTP request",
+ { "Non-HTTP request", NULL,
"GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
SOUP_STATUS_BAD_REQUEST,
NULL, NULL, -1,
{ { NULL } }
},
- { "Junk after Request-Line",
+ { "Junk after Request-Line", NULL,
"GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
SOUP_STATUS_BAD_REQUEST,
NULL, NULL, -1,
{ { NULL } }
},
- { "NUL in Method",
+ { "NUL in Method", NULL,
"G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
SOUP_STATUS_BAD_REQUEST,
NULL, NULL, -1,
{ { NULL } }
},
- { "NUL at beginning of Method",
+ { "NUL at beginning of Method", "666316",
"\x00 / HTTP/1.1\r\nHost: example.com\r\n", 35,
SOUP_STATUS_BAD_REQUEST,
NULL, NULL, -1,
{ { NULL } }
},
- { "NUL in Path",
+ { "NUL in Path", NULL,
"GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
SOUP_STATUS_BAD_REQUEST,
NULL, NULL, -1,
{ { NULL } }
},
- { "NUL in header name",
+ { "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",
+ { "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",
+ { "No terminating CRLF", NULL,
"GET / HTTP/1.1\r\nHost: example.com", -1,
SOUP_STATUS_BAD_REQUEST,
NULL, NULL, -1,
{ { NULL } }
},
- { "Unrecognized expectation",
+ { "Unrecognized expectation", NULL,
"GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1,
SOUP_STATUS_EXPECTATION_FAILED,
NULL, NULL, -1,
@@ -450,36 +450,37 @@ static const int num_reqtests = G_N_ELEMENTS (reqtests);
static struct ResponseTest {
const char *description;
+ const char *bugref;
const char *response;
int length;
SoupHTTPVersion version;
guint status_code;
const char *reason_phrase;
- Header headers[4];
+ Header headers[10];
} resptests[] = {
/***********************/
/*** VALID RESPONSES ***/
/***********************/
- { "HTTP 1.0 response w/ no headers",
+ { "HTTP 1.0 response w/ no headers", NULL,
"HTTP/1.0 200 ok\r\n", -1,
SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
{ { NULL } }
},
- { "HTTP 1.1 response w/ no headers",
+ { "HTTP 1.1 response w/ no headers", NULL,
"HTTP/1.1 200 ok\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { NULL } }
},
- { "Response w/ multi-word Reason-Phrase",
+ { "Response w/ multi-word Reason-Phrase", NULL,
"HTTP/1.1 400 bad request\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_BAD_REQUEST, "bad request",
{ { NULL } }
},
- { "Response w/ 1 header",
+ { "Response w/ 1 header", NULL,
"HTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar" },
@@ -487,7 +488,7 @@ static struct ResponseTest {
}
},
- { "Response w/ 2 headers",
+ { "Response w/ 2 headers", NULL,
"HTTP/1.1 200 ok\r\nFoo: bar\r\nBaz: quux\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar" },
@@ -496,7 +497,7 @@ static struct ResponseTest {
}
},
- { "Response w/ same header multiple times",
+ { "Response w/ same header multiple times", NULL,
"HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar, baz, quux" },
@@ -504,7 +505,7 @@ static struct ResponseTest {
}
},
- { "Response w/ no reason phrase",
+ { "Response w/ no reason phrase", NULL,
"HTTP/1.1 200 \r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
{ { "Foo", "bar" },
@@ -512,7 +513,7 @@ static struct ResponseTest {
}
},
- { "Connection header on HTTP/1.0 message",
+ { "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",
{ { "Foo", "bar" },
@@ -521,13 +522,26 @@ static struct ResponseTest {
}
},
+ /* Tests from Cockpit */
+
+ { "Response w/ 3 headers, check case-insensitivity", "722341",
+ "HTTP/1.0 200 ok\r\nHeader1: value3\r\nHeader2: field\r\nHead3: Another \r\n", -1,
+ SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
+ { { "header1", "value3" },
+ { "Header2", "field" },
+ { "hEAD3", "Another" },
+ { "Something else", NULL },
+ { NULL }
+ }
+ },
+
/*****************************/
/*** RECOVERABLE RESPONSES ***/
/*****************************/
/* RFC 2616 section 3.1 says we MUST accept this */
- { "HTTP/01.01 response",
+ { "HTTP/01.01 response", NULL,
"HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar" },
@@ -537,7 +551,7 @@ static struct ResponseTest {
/* RFC 2616 section 19.3 says we SHOULD accept these */
- { "Response w/ LF instead of CRLF after Status-Line",
+ { "Response w/ LF instead of CRLF after Status-Line", NULL,
"HTTP/1.1 200 ok\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar" },
@@ -545,7 +559,7 @@ static struct ResponseTest {
}
},
- { "Response w/ incorrect spacing in Status-Line",
+ { "Response w/ incorrect spacing in Status-Line", NULL,
"HTTP/1.1 200\tok\r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar" },
@@ -553,7 +567,7 @@ static struct ResponseTest {
}
},
- { "Response w/ no reason phrase or preceding SP",
+ { "Response w/ no reason phrase or preceding SP", NULL,
"HTTP/1.1 200\r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
{ { "Foo", "bar" },
@@ -561,7 +575,7 @@ static struct ResponseTest {
}
},
- { "Response w/ no whitespace after status code",
+ { "Response w/ no whitespace after status code", NULL,
"HTTP/1.1 200ok\r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar" },
@@ -570,7 +584,7 @@ static struct ResponseTest {
},
/* Shoutcast support */
- { "Shoutcast server not-quite-HTTP",
+ { "Shoutcast server not-quite-HTTP", "502325",
"ICY 200 OK\r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_0, SOUP_STATUS_OK, "OK",
{ { "Foo", "bar" },
@@ -578,8 +592,7 @@ static struct ResponseTest {
}
},
- /* qv bug 579318, do_bad_header_tests() below */
- { "Response w/ mangled header",
+ { "Response w/ mangled header", "579318",
"HTTP/1.1 200 ok\r\nFoo: one\r\nBar two:2\r\nBaz: three\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "one" },
@@ -588,8 +601,7 @@ static struct ResponseTest {
}
},
- /* qv bug 602863 */
- { "HTTP 1.1 response with leading line break",
+ { "HTTP 1.1 response with leading line break", "602863",
"\nHTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
{ { "Foo", "bar" },
@@ -600,79 +612,79 @@ static struct ResponseTest {
/*** INVALID RESPONSES ***/
/*************************/
- { "Invalid HTTP version",
+ { "Invalid HTTP version", NULL,
"HTTP/1.2 200 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "Non-HTTP response",
+ { "Non-HTTP response", NULL,
"SOUP/1.1 200 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "Non-numeric status code",
+ { "Non-numeric status code", NULL,
"HTTP/1.1 XXX OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "No status code",
+ { "No status code", NULL,
"HTTP/1.1 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "One-digit status code",
+ { "One-digit status code", NULL,
"HTTP/1.1 2 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "Two-digit status code",
+ { "Two-digit status code", NULL,
"HTTP/1.1 20 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "Four-digit status code",
+ { "Four-digit status code", NULL,
"HTTP/1.1 2000 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "Status code < 100",
+ { "Status code < 100", NULL,
"HTTP/1.1 001 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "Status code > 599",
+ { "Status code > 599", NULL,
"HTTP/1.1 600 OK\r\nFoo: bar\r\n", -1,
-1, 0, NULL,
{ { NULL } }
},
- { "NUL at start",
+ { "NUL at start", "666316",
"\x00HTTP/1.1 200 OK\r\nFoo: bar\r\n", 28,
-1, 0, NULL,
{ { NULL } }
},
- { "NUL in Reason Phrase",
+ { "NUL in Reason Phrase", NULL,
"HTTP/1.1 200 O\x00K\r\nFoo: bar\r\n", 28,
-1, 0, NULL,
{ { NULL } }
},
- { "NUL in header name",
+ { "NUL in header name", NULL,
"HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
-1, 0, NULL,
{ { NULL } }
},
- { "NUL in header value",
+ { "NUL in header value", NULL,
"HTTP/1.1 200 OK\r\nFoo: b\x00ar\r\n", 28,
-1, 0, NULL,
{ { NULL } }
@@ -704,18 +716,11 @@ static struct QValueTest {
static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
static void
-print_header (const char *name, const char *value, gpointer data)
-{
- debug_printf (1, " '%s': '%s'\n", name, value);
-}
-
-static gboolean
check_headers (Header *headers, SoupMessageHeaders *hdrs)
{
GSList *header_names, *h;
SoupMessageHeadersIter iter;
const char *name, *value;
- gboolean ok = TRUE;
int i;
header_names = NULL;
@@ -727,36 +732,33 @@ check_headers (Header *headers, SoupMessageHeaders *hdrs)
}
for (i = 0, h = header_names; headers[i].name && h; i++, h = h->next) {
- if (strcmp (h->data, headers[i].name) != 0) {
- ok = FALSE;
- break;
- }
+ g_assert (g_ascii_strcasecmp (h->data, headers[i].name) == 0);
+
value = soup_message_headers_get_list (hdrs, headers[i].name);
- if (strcmp (value, headers[i].value) != 0) {
- ok = FALSE;
- break;
- }
+ g_assert_cmpstr (value, ==, headers[i].value);
+ }
+ /* If we have remaining fields to check, they should return NULL */
+ for (; headers[i].name; i++) {
+ value = soup_message_headers_get_list (hdrs, headers[i].name);
+ g_assert_null (value);
}
- if (headers[i].name || h)
- ok = FALSE;
+ g_assert_null (headers[i].name);
+ g_assert_null (h);
+
g_slist_free (header_names);
- return ok;
}
static void
do_request_tests (void)
{
- int i, len, h;
+ int i, len;
char *method, *path;
SoupHTTPVersion version;
SoupMessageHeaders *headers;
guint status;
- debug_printf (1, "Request tests\n");
for (i = 0; i < num_reqtests; i++) {
- gboolean ok = TRUE;
-
- debug_printf (1, "%2d. %s (%s): ", i + 1, reqtests[i].description,
+ debug_printf (1, "%2d. %s (%s)\n", i + 1, reqtests[i].description,
soup_status_get_phrase (reqtests[i].status));
headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
@@ -769,71 +771,32 @@ do_request_tests (void)
status = soup_headers_parse_request (reqtests[i].request, len,
headers, &method, &path,
&version);
+ g_assert_cmpint (status, ==, reqtests[i].status);
if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- if ((reqtests[i].method && strcmp (reqtests[i].method, method) != 0) || !reqtests[i].method)
- ok = FALSE;
- if ((reqtests[i].path && strcmp (reqtests[i].path, path) != 0) || !reqtests[i].path)
- ok = FALSE;
- if (reqtests[i].version != version)
- ok = FALSE;
-
- if (!check_headers (reqtests[i].headers, headers))
- ok = FALSE;
- } else {
- if (status != reqtests[i].status)
- ok = FALSE;
- }
+ g_assert_cmpstr (method, ==, reqtests[i].method);
+ g_assert_cmpstr (path, ==, reqtests[i].path);
+ g_assert_cmpint (version, ==, reqtests[i].version);
- if (ok)
- debug_printf (1, "OK!\n");
- else {
- debug_printf (1, "BAD!\n");
- errors++;
- if (reqtests[i].method) {
- debug_printf (1, " expected: '%s' '%s' 'HTTP/1.%d'\n",
- reqtests[i].method,
- reqtests[i].path,
- reqtests[i].version);
- for (h = 0; reqtests[i].headers[h].name; h++) {
- debug_printf (1, " '%s': '%s'\n",
- reqtests[i].headers[h].name,
- reqtests[i].headers[h].value);
- }
- } else {
- debug_printf (1, " expected: %s\n",
- soup_status_get_phrase (reqtests[i].status));
- }
- if (method) {
- debug_printf (1, " got: '%s' '%s' 'HTTP/1.%d'\n",
- method, path, version);
- soup_message_headers_foreach (headers, print_header, NULL);
- } else {
- debug_printf (1, " got: %s\n",
- soup_status_get_phrase (status));
- }
+ check_headers (reqtests[i].headers, headers);
}
g_free (method);
g_free (path);
soup_message_headers_free (headers);
}
- debug_printf (1, "\n");
}
static void
do_response_tests (void)
{
- int i, len, h;
+ int i, len;
guint status_code;
char *reason_phrase;
SoupHTTPVersion version;
SoupMessageHeaders *headers;
- debug_printf (1, "Response tests\n");
for (i = 0; i < num_resptests; i++) {
- gboolean ok = TRUE;
-
- debug_printf (1, "%2d. %s (%s): ", i + 1, resptests[i].description,
+ debug_printf (1, "%2d. %s (%s)\n", i + 1, resptests[i].description,
resptests[i].reason_phrase ? "should parse" : "should NOT parse");
headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
@@ -846,49 +809,17 @@ do_response_tests (void)
if (soup_headers_parse_response (resptests[i].response, len,
headers, &version,
&status_code, &reason_phrase)) {
- if (resptests[i].version != version)
- ok = FALSE;
- if (resptests[i].status_code != status_code)
- ok = FALSE;
- if ((resptests[i].reason_phrase && strcmp (resptests[i].reason_phrase, reason_phrase) != 0) || !resptests[i].reason_phrase)
- ok = FALSE;
-
- if (!check_headers (resptests[i].headers, headers))
- ok = FALSE;
- } else {
- if (resptests[i].reason_phrase)
- ok = FALSE;
- }
+ g_assert_cmpint (version, ==, resptests[i].version);
+ g_assert_cmpint (status_code, ==, resptests[i].status_code);
+ g_assert_cmpstr (reason_phrase, ==, resptests[i].reason_phrase);
- if (ok)
- debug_printf (1, "OK!\n");
- else {
- debug_printf (1, "BAD!\n");
- errors++;
- if (resptests[i].reason_phrase) {
- debug_printf (1, " expected: 'HTTP/1.%d' '%03d' '%s'\n",
- resptests[i].version,
- resptests[i].status_code,
- resptests[i].reason_phrase);
- for (h = 0; resptests[i].headers[h].name; h++) {
- debug_printf (1, " '%s': '%s'\n",
- resptests[i].headers[h].name,
- resptests[i].headers[h].value);
- }
- } else
- debug_printf (1, " expected: parse error\n");
- if (reason_phrase) {
- debug_printf (1, " got: 'HTTP/1.%d' '%03d' '%s'\n",
- version, status_code, reason_phrase);
- soup_message_headers_foreach (headers, print_header, NULL);
- } else
- debug_printf (1, " got: parse error\n");
- }
+ check_headers (resptests[i].headers, headers);
+ } else
+ g_assert_null (resptests[i].reason_phrase);
g_free (reason_phrase);
soup_message_headers_free (headers);
}
- debug_printf (1, "\n");
}
static void
@@ -896,9 +827,7 @@ do_qvalue_tests (void)
{
int i, j;
GSList *acceptable, *unacceptable, *iter;
- gboolean wrong;
- debug_printf (1, "qvalue tests\n");
for (i = 0; i < num_qvaluetests; i++) {
debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value);
@@ -907,48 +836,26 @@ do_qvalue_tests (void)
&unacceptable);
debug_printf (1, " acceptable: ");
- wrong = FALSE;
if (acceptable) {
for (iter = acceptable, j = 0; iter; iter = iter->next, j++) {
debug_printf (1, "%s ", (char *)iter->data);
- if (!qvaluetests[i].acceptable[j] ||
- strcmp (iter->data, qvaluetests[i].acceptable[j]) != 0)
- wrong = TRUE;
+ g_assert_cmpstr (iter->data, ==, qvaluetests[i].acceptable[j]);
}
debug_printf (1, "\n");
soup_header_free_list (acceptable);
} else
debug_printf (1, "(none)\n");
- if (wrong) {
- debug_printf (1, " WRONG! expected: ");
- for (j = 0; qvaluetests[i].acceptable[j]; j++)
- debug_printf (1, "%s ", qvaluetests[i].acceptable[j]);
- debug_printf (1, "\n");
- errors++;
- }
debug_printf (1, " unacceptable: ");
- wrong = FALSE;
if (unacceptable) {
for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) {
debug_printf (1, "%s ", (char *)iter->data);
- if (!qvaluetests[i].unacceptable[j] ||
- strcmp (iter->data, qvaluetests[i].unacceptable[j]) != 0)
- wrong = TRUE;
+ g_assert_cmpstr (iter->data, ==, qvaluetests[i].unacceptable[j]);
}
debug_printf (1, "\n");
soup_header_free_list (unacceptable);
} else
debug_printf (1, "(none)\n");
- if (wrong) {
- debug_printf (1, " WRONG! expected: ");
- for (j = 0; qvaluetests[i].unacceptable[j]; j++)
- debug_printf (1, "%s ", qvaluetests[i].unacceptable[j]);
- debug_printf (1, "\n");
- errors++;
- }
-
- debug_printf (1, "\n");
}
}
@@ -972,8 +879,6 @@ do_content_disposition_tests (void)
SoupMultipart *multipart;
SoupMessageBody *body;
- debug_printf (1, "Content-Disposition tests\n");
-
hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
params = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (params, "filename", RFC5987_TEST_FILENAME);
@@ -981,13 +886,7 @@ do_content_disposition_tests (void)
g_hash_table_destroy (params);
header = soup_message_headers_get_one (hdrs, "Content-Disposition");
- if (!strcmp (header, RFC5987_TEST_HEADER_ENCODED))
- debug_printf (1, " encoded OK\n");
- else {
- debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
- RFC5987_TEST_HEADER_ENCODED, header);
- errors++;
- }
+ g_assert_cmpstr (header, ==, RFC5987_TEST_HEADER_ENCODED);
/* UTF-8 decoding */
soup_message_headers_clear (hdrs);
@@ -996,22 +895,13 @@ do_content_disposition_tests (void)
if (!soup_message_headers_get_content_disposition (hdrs,
&disposition,
&params)) {
- debug_printf (1, " UTF-8 decoding FAILED!\n could not parse\n");
- errors++;
+ soup_test_assert (FALSE, "UTF-8 decoding FAILED");
return;
}
g_free (disposition);
filename = g_hash_table_lookup (params, "filename");
- if (!filename) {
- debug_printf (1, " UTF-8 decoding FAILED!\n could not find filename\n");
- errors++;
- } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
- debug_printf (1, " UTF-8 decoding FAILED!\n expected: %s\n got: %s\n",
- RFC5987_TEST_FILENAME, filename);
- errors++;
- } else
- debug_printf (1, " UTF-8 decoded OK\n");
+ g_assert_cmpstr (filename, ==, RFC5987_TEST_FILENAME);
g_hash_table_destroy (params);
/* ISO-8859-1 decoding */
@@ -1021,22 +911,13 @@ do_content_disposition_tests (void)
if (!soup_message_headers_get_content_disposition (hdrs,
&disposition,
&params)) {
- debug_printf (1, " iso-8859-1 decoding FAILED!\n could not parse\n");
- errors++;
+ soup_test_assert (FALSE, "iso-8859-1 decoding FAILED");
return;
}
g_free (disposition);
filename = g_hash_table_lookup (params, "filename");
- if (!filename) {
- debug_printf (1, " iso-8859-1 decoding FAILED!\n could not find filename\n");
- errors++;
- } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
- debug_printf (1, " iso-8859-1 decoding FAILED!\n expected: %s\n got: %s\n",
- RFC5987_TEST_FILENAME, filename);
- errors++;
- } else
- debug_printf (1, " iso-8859-1 decoded OK\n");
+ g_assert_cmpstr (filename, ==, RFC5987_TEST_FILENAME);
g_hash_table_destroy (params);
/* Fallback */
@@ -1046,27 +927,19 @@ do_content_disposition_tests (void)
if (!soup_message_headers_get_content_disposition (hdrs,
&disposition,
&params)) {
- debug_printf (1, " fallback decoding FAILED!\n could not parse\n");
- errors++;
+ soup_test_assert (FALSE, "fallback decoding FAILED");
return;
}
g_free (disposition);
filename = g_hash_table_lookup (params, "filename");
- if (!filename) {
- debug_printf (1, " fallback decoding FAILED!\n could not find filename\n");
- errors++;
- } else if (strcmp (filename, RFC5987_TEST_FALLBACK_FILENAME) != 0) {
- debug_printf (1, " fallback decoding FAILED!\n expected: %s\n got: %s\n",
- RFC5987_TEST_FALLBACK_FILENAME, filename);
- errors++;
- } else
- debug_printf (1, " fallback decoded OK\n");
+ g_assert_cmpstr (filename, ==, RFC5987_TEST_FALLBACK_FILENAME);
g_hash_table_destroy (params);
soup_message_headers_free (hdrs);
- /* Ensure that soup-multipart always quotes filename (bug 641280) */
+ /* Ensure that soup-multipart always quotes filename */
+ g_test_bug ("641280");
multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
buffer = soup_buffer_new (SOUP_MEMORY_STATIC, "foo", 3);
soup_multipart_append_form_file (multipart, "test", "token",
@@ -1082,15 +955,9 @@ do_content_disposition_tests (void)
buffer = soup_message_body_flatten (body);
soup_message_body_free (body);
- if (strstr (buffer->data, "filename=\"token\""))
- debug_printf (1, " SoupMultipart encoded filename correctly\n");
- else {
- debug_printf (1, " SoupMultipart encoded filename incorrectly!\n");
- errors++;
- }
- soup_buffer_free (buffer);
+ g_assert_true (strstr (buffer->data, "filename=\"token\""));
- debug_printf (1, "\n");
+ soup_buffer_free (buffer);
}
#define CONTENT_TYPE_TEST_MIME_TYPE "text/plain"
@@ -1107,7 +974,7 @@ do_content_type_tests (void)
GHashTable *params;
const char *header, *mime_type;
- debug_printf (1, "Content-Type tests\n");
+ g_test_bug ("576760");
hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
params = g_hash_table_new (g_str_hash, g_str_equal);
@@ -1117,13 +984,7 @@ do_content_type_tests (void)
g_hash_table_destroy (params);
header = soup_message_headers_get_one (hdrs, "Content-Type");
- if (!strcmp (header, CONTENT_TYPE_TEST_HEADER))
- debug_printf (1, " encoded OK\n");
- else {
- debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
- CONTENT_TYPE_TEST_HEADER, header);
- errors++;
- }
+ g_assert_cmpstr (header, ==, CONTENT_TYPE_TEST_HEADER);
soup_message_headers_clear (hdrs);
soup_message_headers_append (hdrs, "Content-Type",
@@ -1133,38 +994,20 @@ do_content_type_tests (void)
CONTENT_TYPE_TEST_MIME_TYPE);
mime_type = soup_message_headers_get_content_type (hdrs, &params);
- if (!mime_type) {
- debug_printf (1, " decoding FAILED!\n could not parse\n");
- errors++;
- }
-
- if (mime_type && strcmp (mime_type, CONTENT_TYPE_TEST_MIME_TYPE) != 0) {
- debug_printf (1, " decoding FAILED!\n bad returned MIME type: %s\n",
- mime_type);
- errors++;
- } else if (params && g_hash_table_size (params) != 0) {
- debug_printf (1, " decoding FAILED!\n params contained %d params (should be 0)\n",
- g_hash_table_size (params));
- errors++;
- } else
- debug_printf (1, " decoded OK\n");
-
+ g_assert_cmpstr (mime_type, ==, CONTENT_TYPE_TEST_MIME_TYPE);
+ g_assert_cmpint (g_hash_table_size (params), ==, 0);
if (params)
g_hash_table_destroy (params);
+ g_test_bug ("577630");
+
soup_message_headers_clear (hdrs);
soup_message_headers_append (hdrs, "Content-Type",
CONTENT_TYPE_BAD_HEADER);
mime_type = soup_message_headers_get_content_type (hdrs, &params);
- if (mime_type) {
- debug_printf (1, " Bad content rejection FAILED!\n");
- errors++;
- } else
- debug_printf (1, " Bad content rejection OK\n");
+ g_assert_null (mime_type);
soup_message_headers_free (hdrs);
-
- debug_printf (1, "\n");
}
struct {
@@ -1185,7 +1028,7 @@ do_append_param_tests (void)
GString *params;
int i;
- debug_printf (1, "soup_header_g_string_append_param() tests\n");
+ g_test_bug ("577728");
params = g_string_new (NULL);
for (i = 0; i < G_N_ELEMENTS (test_params); i++) {
@@ -1195,15 +1038,8 @@ do_append_param_tests (void)
test_params[i].name,
test_params[i].value);
}
- if (strcmp (params->str, TEST_PARAMS_RESULT) != 0) {
- debug_printf (1, " FAILED!\n expected: %s\n got: %s\n",
- TEST_PARAMS_RESULT, params->str);
- errors++;
- } else
- debug_printf (1, " OK\n");
+ g_assert_cmpstr (params->str, ==, TEST_PARAMS_RESULT);
g_string_free (params, TRUE);
-
- debug_printf (1, "\n");
}
static const struct {
@@ -1226,19 +1062,15 @@ do_bad_header_tests (void)
SoupMessageHeaders *hdrs;
int i;
- debug_printf (1, "bad header rejection tests\n");
-
hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
for (i = 0; i < G_N_ELEMENTS (bad_headers); i++) {
debug_printf (1, " %s\n", bad_headers[i].description);
- expect_warning = TRUE;
+
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_CRITICAL,
+ "*soup_message_headers_append*assertion*failed*");
soup_message_headers_append (hdrs, bad_headers[i].name,
bad_headers[i].value);
- if (expect_warning) {
- expect_warning = FALSE;
- debug_printf (1, " FAILED: soup_message_headers_append() did not reject it\n");
- errors++;
- }
+ g_test_assert_expected_messages ();
}
soup_message_headers_free (hdrs);
}
@@ -1246,16 +1078,20 @@ do_bad_header_tests (void)
int
main (int argc, char **argv)
{
+ int ret;
+
test_init (argc, argv, NULL);
- do_request_tests ();
- do_response_tests ();
- do_qvalue_tests ();
- do_content_disposition_tests ();
- do_content_type_tests ();
- do_append_param_tests ();
- do_bad_header_tests ();
+ g_test_add_func ("/header-parsing/request", do_request_tests);
+ g_test_add_func ("/header-parsing/response", do_response_tests);
+ g_test_add_func ("/header-parsing/qvalue", do_qvalue_tests);
+ g_test_add_func ("/header-parsing/content-disposition", do_content_disposition_tests);
+ g_test_add_func ("/header-parsing/content-type", do_content_type_tests);
+ g_test_add_func ("/header-parsing/append-param", do_append_param_tests);
+ g_test_add_func ("/header-parsing/bad", do_bad_header_tests);
+
+ ret = g_test_run ();
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/httpd.conf.in b/tests/httpd.conf.22.in
index de0b75fa..b912ca36 100644
--- a/tests/httpd.conf.in
+++ b/tests/httpd.conf.22.in
@@ -3,8 +3,7 @@
ServerName 127.0.0.1
Listen 127.0.0.1:47524
-PidFile @builddir@/httpd.pid
-DocumentRoot @srcdir@
+DocumentRoot .
# The tests shut down apache with "graceful-stop", because that makes
# it close its listening socket right away. But it seems to sometimes
@@ -83,7 +82,7 @@ Listen 127.0.0.1:47527
AuthType Basic
AuthName realm1
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require valid-user
</Proxy>
@@ -94,7 +93,7 @@ Listen 127.0.0.1:47527
AuthType Basic
AuthName realm1
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require valid-user
</Proxy>
@@ -124,7 +123,7 @@ Listen 127.0.0.1:47528
AuthType Basic
AuthName realm1
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user no-such-user
</Proxy>
@@ -135,7 +134,7 @@ Listen 127.0.0.1:47528
AuthType Basic
AuthName realm1
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user no-such-user
</Proxy>
@@ -154,89 +153,89 @@ Listen 127.0.0.1:47528
<VirtualHost 127.0.0.1:47525>
SSLEngine on
- SSLCertificateFile @srcdir@/test-cert.pem
- SSLCertificateKeyFile @srcdir@/test-key.pem
+ SSLCertificateFile ./test-cert.pem
+ SSLCertificateKeyFile ./test-key.pem
</VirtualHost>
</IfModule>
# Basic auth tests
-Alias /Basic/realm1/realm2/realm1 @srcdir@
-Alias /Basic/realm1/realm2 @srcdir@
-Alias /Basic/realm1/subdir @srcdir@
-Alias /Basic/realm1/not @srcdir@
-Alias /Basic/realm1 @srcdir@
-Alias /Basic/realm12/subdir @srcdir@
-Alias /Basic/realm12 @srcdir@
-Alias /Basic/realm2 @srcdir@
-Alias /Basic/realm3 @srcdir@
-Alias /Basic @srcdir@
+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 @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user user1
</Location>
<Location /Basic/realm1/not>
AuthType Basic
AuthName realm1
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user user2
</Location>
<Location /Basic/realm12>
AuthType Basic
AuthName realm12
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user user1 user2
</Location>
<Location /Basic/realm1/realm2>
AuthType Basic
AuthName realm2
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user user2
</Location>
<Location /Basic/realm1/realm2/realm1>
AuthType Basic
AuthName realm1
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user user1
</Location>
<Location /Basic/realm2>
AuthType Basic
AuthName realm2
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user user2
</Location>
<Location /Basic/realm3>
AuthType Basic
AuthName realm3
- AuthUserFile @srcdir@/htpasswd
+ AuthUserFile ./htpasswd
Require user user3
</Location>
# Digest auth tests
-Alias /Digest/realm1/realm2/realm1 @srcdir@
-Alias /Digest/realm1/realm2 @srcdir@
-Alias /Digest/realm1/subdir @srcdir@
-Alias /Digest/realm1/expire @srcdir@
-Alias /Digest/realm1/not @srcdir@
-Alias /Digest/realm1 @srcdir@
-Alias /Digest/realm2 @srcdir@
-Alias /Digest/realm3 @srcdir@
-Alias /Digest @srcdir@
+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 @srcdir@/htdigest
+ AuthUserFile ./htdigest
AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
Require valid-user
</Location>
@@ -244,7 +243,7 @@ Alias /Digest @srcdir@
<Location /Digest/realm1/expire>
AuthType Digest
AuthName realm1
- AuthUserFile @srcdir@/htdigest
+ AuthUserFile ./htdigest
AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
AuthDigestNonceLifetime 2
Require valid-user
@@ -253,7 +252,7 @@ Alias /Digest @srcdir@
<Location /Digest/realm1/not>
AuthType Digest
AuthName realm1
- AuthUserFile @srcdir@/htdigest
+ AuthUserFile ./htdigest
AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
Require user user2
</Location>
@@ -261,7 +260,7 @@ Alias /Digest @srcdir@
<Location /Digest/realm1/realm2>
AuthType Digest
AuthName realm2
- AuthUserFile @srcdir@/htdigest
+ AuthUserFile ./htdigest
AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2
Require valid-user
</Location>
@@ -269,7 +268,7 @@ Alias /Digest @srcdir@
<Location /Digest/realm1/realm2/realm1>
AuthType Digest
AuthName realm1
- AuthUserFile @srcdir@/htdigest
+ AuthUserFile ./htdigest
AuthDigestDomain /Digest/realm1 /Digest/realm1/realm2/realm1
Require valid-user
</Location>
@@ -277,7 +276,7 @@ Alias /Digest @srcdir@
<Location /Digest/realm2>
AuthType Digest
AuthName realm2
- AuthUserFile @srcdir@/htdigest
+ AuthUserFile ./htdigest
AuthDigestDomain /Digest/realm2 /Digest/realm1/realm2
Require valid-user
</Location>
@@ -285,7 +284,7 @@ Alias /Digest @srcdir@
<Location /Digest/realm3>
AuthType Digest
AuthName realm3
- AuthUserFile @srcdir@/htdigest
+ AuthUserFile ./htdigest
AuthDigestDomain /Digest/realm3
Require valid-user
# test RFC2069-style Digest
diff --git a/tests/httpd.conf.24.in b/tests/httpd.conf.24.in
new file mode 100644
index 00000000..850b8393
--- /dev/null
+++ b/tests/httpd.conf.24.in
@@ -0,0 +1,276 @@
+# 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 mpm_prefork_module @APACHE_MODULE_DIR@/mod_mpm_prefork.so
+LoadModule alias_module @APACHE_MODULE_DIR@/mod_alias.so
+LoadModule auth_basic_module @APACHE_MODULE_DIR@/mod_auth_basic.so
+LoadModule auth_digest_module @APACHE_MODULE_DIR@/mod_auth_digest.so
+LoadModule authn_core_module @APACHE_MODULE_DIR@/mod_authn_core.so
+LoadModule authn_file_module @APACHE_MODULE_DIR@/mod_authn_file.so
+LoadModule authz_core_module @APACHE_MODULE_DIR@/mod_authz_core.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
+LoadModule unixd_module @APACHE_SSL_MODULE_DIR@/mod_unixd.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 *>
+ Require all denied
+ </Proxy>
+
+ # Allow local http connections
+ <Proxy http://127.0.0.1*>
+ Require all granted
+ </Proxy>
+
+ # Allow CONNECT to local https port
+ <Proxy 127.0.0.1:47525>
+ Require all granted
+ </Proxy>
+
+ # Deny non-proxy requests
+ <Directory />
+ Require all denied
+ </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 *>
+ Require all denied
+ </Proxy>
+
+ # Allow local http connections with authentication
+ <Proxy http://127.0.0.1:47524*>
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile ./htpasswd
+ Require valid-user
+ </Proxy>
+
+ # Allow CONNECT to local https port with authentication
+ <Proxy 127.0.0.1:47525>
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile ./htpasswd
+ Require valid-user
+ </Proxy>
+
+ # Fail non-proxy requests
+ <Directory />
+ Require all denied
+ </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 *>
+ Require all denied
+ </Proxy>
+
+ # Allow local http connections with authentication
+ <Proxy http://127.0.0.1:47524*>
+ 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>
+ AuthType Basic
+ AuthName realm1
+ AuthUserFile ./htpasswd
+ Require user no-such-user
+ </Proxy>
+
+ # Fail non-proxy requests
+ <Directory />
+ Require all denied
+ </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/libsoup.supp b/tests/libsoup.supp
index 24c6516a..ae8bda38 100644
--- a/tests/libsoup.supp
+++ b/tests/libsoup.supp
@@ -321,7 +321,7 @@
...
fun:_dl_allocate_tls
...
- fun:g_thread_create_posix_impl
+ fun:g_system_thread_new
}
{
glib/filenamecharsets
@@ -342,12 +342,78 @@
fun:g_tls_backend_gnutls_store_session
}
{
+ glib/gtlssessioncache_client
+ Memcheck:Leak
+ ...
+ fun:gnutls_session_get_data2
+ fun:g_tls_client_connection_gnutls_finish_handshake
+}
+{
+ glib/gtlssessioncache_client2
+ Memcheck:Leak
+ ...
+ fun:g_bytes_new_with_free_func
+ fun:g_tls_client_connection_gnutls_finish_handshake
+}
+{
+ glib/gtlssessioncache_client3
+ Memcheck:Leak
+ ...
+ fun:g_bytes_new_take
+ fun:g_tls_client_connection_gnutls_constructed
+}
+{
+ glib/gtlssessioncache_client4
+ Memcheck:Leak
+ ...
+ fun:g_strdup_printf
+ fun:g_tls_client_connection_gnutls_constructed
+}
+{
+ glib/gtlssessioncache_server
+ Memcheck:Leak
+ ...
+ fun:g_tls_server_connection_gnutls_db_store
+}
+{
glib/cached_poll_array
Memcheck:Leak
...
fun:g_malloc_n
fun:g_main_context_iterate
}
+{
+ glib/rand
+ Memcheck:Leak
+ ...
+ fun:g_rand_new
+ fun:g_random_int_range
+}
+{
+ glib/g_cancellable_push_current
+ Memcheck:Leak
+ ...
+ fun:g_cancellable_push_current
+}
+{
+ glib/slice_thread_local
+ Memcheck:Leak
+ ...
+ fun:thread_memory_from_self
+}
+{
+ glib/gobjectinit
+ Memcheck:Leak
+ ...
+ fun:gobject_init_ctor
+}
+{
+ glib/gtask threadpool
+ Memcheck:Leak
+ ...
+ fun:g_thread_pool_new
+ fun:g_task_thread_pool_init
+}
# probably inlines the aggressive memcpy/memcmp
{
@@ -421,6 +487,12 @@
...
fun:intern_header_name
}
+{
+ libsoup/tlds
+ Memcheck:Leak
+ ...
+ fun:soup_tld_ensure_rules_hash_table
+}
# fixme?
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 605fa216..00559a03 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -32,16 +32,6 @@ server_callback (SoupServer *server, SoupMessage *msg,
SoupURI *uri = soup_message_get_uri (msg);
const char *server_protocol = data;
- soup_message_headers_append (msg->response_headers,
- "X-Handled-By", "server_callback");
-
- if (!strcmp (path, "*")) {
- debug_printf (1, " default server_callback got request for '*'!\n");
- errors++;
- soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
- return;
- }
-
if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
return;
@@ -99,32 +89,8 @@ server_callback (SoupServer *server, SoupMessage *msg,
}
}
-static void
-server_star_callback (SoupServer *server, SoupMessage *msg,
- const char *path, GHashTable *query,
- SoupClientContext *context, gpointer data)
-{
- soup_message_headers_append (msg->response_headers,
- "X-Handled-By", "star_callback");
-
- if (strcmp (path, "*") != 0) {
- debug_printf (1, " server_star_callback got request for '%s'!\n", path);
- errors++;
- soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
- return;
- }
-
- if (msg->method != SOUP_METHOD_OPTIONS) {
- soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
- return;
- }
-
- soup_message_set_status (msg, SOUP_STATUS_OK);
-}
-
/* Host header handling: client must be able to override the default
* value, server must be able to recognize different Host values.
- * #539803.
*/
static void
do_host_test (void)
@@ -132,7 +98,7 @@ do_host_test (void)
SoupSession *session;
SoupMessage *one, *two;
- debug_printf (1, "Host handling\n");
+ g_test_bug ("539803");
session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
@@ -145,42 +111,24 @@ do_host_test (void)
soup_test_session_abort_unref (session);
- if (!SOUP_STATUS_IS_SUCCESSFUL (one->status_code)) {
- debug_printf (1, " Message 1 failed: %d %s\n",
- one->status_code, one->reason_phrase);
- errors++;
- } else if (strcmp (one->response_body->data, "index") != 0) {
- debug_printf (1, " Unexpected response to message 1: '%s'\n",
- one->response_body->data);
- errors++;
- }
+ soup_test_assert_message_status (one, SOUP_STATUS_OK);
+ g_assert_cmpstr (one->response_body->data, ==, "index");
g_object_unref (one);
- if (!SOUP_STATUS_IS_SUCCESSFUL (two->status_code)) {
- debug_printf (1, " Message 2 failed: %d %s\n",
- two->status_code, two->reason_phrase);
- errors++;
- } else if (strcmp (two->response_body->data, "foo-index") != 0) {
- debug_printf (1, " Unexpected response to message 2: '%s'\n",
- two->response_body->data);
- errors++;
- }
+ soup_test_assert_message_status (two, SOUP_STATUS_OK);
+ g_assert_cmpstr (two->response_body->data, ==, "foo-index");
g_object_unref (two);
}
/* 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.) #533473
+ * (This test will crash if it fails.)
*/
static void
cu_one_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
{
debug_printf (2, " Message 1 completed\n");
- if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
- debug_printf (1, " Unexpected status on Message 1: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CANT_CONNECT);
g_object_unref (session);
}
@@ -195,11 +143,7 @@ static void
cu_two_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
{
debug_printf (2, " Message 2 completed\n");
- if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
- debug_printf (1, " Unexpected status on Message 2: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CANT_CONNECT);
g_idle_add (cu_idle_quit, loop);
}
@@ -213,7 +157,7 @@ do_callback_unref_test (void)
GMainLoop *loop;
char *bad_uri;
- debug_printf (1, "\nCallback unref handling (msg api)\n");
+ g_test_bug ("533473");
/* Get a guaranteed-bad URI */
addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
@@ -243,22 +187,19 @@ do_callback_unref_test (void)
g_main_loop_run (loop);
g_main_loop_unref (loop);
+ g_assert_null (session);
if (session) {
g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
- debug_printf (1, " Session not destroyed?\n");
- errors++;
g_object_unref (session);
}
+ g_assert_null (one);
if (one) {
g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
- debug_printf (1, " Message 1 not destroyed?\n");
- errors++;
g_object_unref (one);
}
+ g_assert_null (two);
if (two) {
g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
- debug_printf (1, " Message 2 not destroyed?\n");
- errors++;
g_object_unref (two);
}
@@ -272,14 +213,8 @@ cur_one_completed (GObject *source, GAsyncResult *result, gpointer session)
GError *error = NULL;
debug_printf (2, " Request 1 completed\n");
- if (soup_request_send_finish (one, result, &error)) {
- debug_printf (1, " Request 1 succeeded?\n");
- errors++;
- } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
- debug_printf (1, " Unexpected error on Request 1: %s\n",
- error->message);
- errors++;
- }
+ soup_request_send_finish (one, result, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED);
g_clear_error (&error);
g_object_unref (session);
@@ -299,14 +234,8 @@ cur_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
GError *error = NULL;
debug_printf (2, " Request 2 completed\n");
- if (soup_request_send_finish (two, result, &error)) {
- debug_printf (1, " Request 2 succeeded?\n");
- errors++;
- } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
- debug_printf (1, " Unexpected error on Request 2: %s\n",
- error->message);
- errors++;
- }
+ soup_request_send_finish (two, result, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED);
g_clear_error (&error);
g_idle_add (cur_idle_quit, loop);
@@ -318,13 +247,10 @@ do_callback_unref_req_test (void)
SoupServer *bad_server;
SoupAddress *addr;
SoupSession *session;
- SoupRequester *requester;
SoupRequest *one, *two;
GMainLoop *loop;
char *bad_uri;
- debug_printf (1, "\nCallback unref handling (request api)\n");
-
/* Get a guaranteed-bad URI */
addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
soup_address_resolve_sync (addr, NULL);
@@ -337,18 +263,15 @@ do_callback_unref_req_test (void)
g_object_unref (bad_server);
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
-
loop = g_main_loop_new (NULL, TRUE);
- one = soup_requester_request (requester, bad_uri, NULL);
+ one = soup_session_request (session, bad_uri, NULL);
g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
- two = soup_requester_request (requester, bad_uri, NULL);
+ two = soup_session_request (session, bad_uri, NULL);
g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
g_free (bad_uri);
@@ -360,22 +283,19 @@ do_callback_unref_req_test (void)
g_main_loop_run (loop);
g_main_loop_unref (loop);
+ g_assert_null (session);
if (session) {
g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
- debug_printf (1, " Session not destroyed?\n");
- errors++;
g_object_unref (session);
}
+ g_assert_null (one);
if (one) {
g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
- debug_printf (1, " Request 1 not destroyed?\n");
- errors++;
g_object_unref (one);
}
+ g_assert_null (two);
if (two) {
g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
- debug_printf (1, " Request 2 not destroyed?\n");
- errors++;
g_object_unref (two);
}
@@ -384,20 +304,19 @@ do_callback_unref_req_test (void)
/* SoupSession should clean up all signal handlers on a message after
* it is finished, allowing the message to be reused if desired.
- * #559054
*/
static void
ensure_no_signal_handlers (SoupMessage *msg, guint *signal_ids, guint n_signal_ids)
{
int i;
+ guint id;
for (i = 0; i < n_signal_ids; i++) {
- if (g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i],
- 0, NULL, NULL, NULL)) {
- debug_printf (1, " Message has handler for '%s'\n",
- g_signal_name (signal_ids[i]));
- errors++;
- }
+ id = g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i],
+ 0, NULL, NULL, NULL);
+ soup_test_assert (id == 0,
+ "message has handler for '%s'",
+ g_signal_name (signal_ids[i]));
}
}
@@ -420,7 +339,7 @@ do_msg_reuse_test (void)
SoupURI *uri;
guint *signal_ids, n_signal_ids;
- debug_printf (1, "\nSoupMessage reuse\n");
+ g_test_bug ("559054");
signal_ids = g_signal_list_ids (SOUP_TYPE_MESSAGE, &n_signal_ids);
@@ -438,10 +357,7 @@ do_msg_reuse_test (void)
soup_message_set_uri (msg, uri);
soup_uri_free (uri);
soup_session_send_message (session, msg);
- if (!soup_uri_equal (soup_message_get_uri (msg), base_uri)) {
- debug_printf (1, " Message did not get redirected!\n");
- errors++;
- }
+ g_assert_true (soup_uri_equal (soup_message_get_uri (msg), base_uri));
ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
debug_printf (1, " Auth message\n");
@@ -449,10 +365,7 @@ do_msg_reuse_test (void)
soup_message_set_uri (msg, uri);
soup_uri_free (uri);
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " Message did not get authenticated!\n");
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
/* One last try to make sure the auth stuff got cleaned up */
@@ -466,79 +379,12 @@ do_msg_reuse_test (void)
g_free (signal_ids);
}
-/* Server handlers for "*" work but are separate from handlers for
- * all other URIs. #590751
- */
-static void
-do_star_test (void)
-{
- SoupSession *session;
- SoupMessage *msg;
- SoupURI *star_uri;
- const char *handled_by;
-
- debug_printf (1, "\nOPTIONS *\n");
-
- session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
- star_uri = soup_uri_copy (base_uri);
- soup_uri_set_path (star_uri, "*");
-
- debug_printf (1, " Testing with no handler\n");
- msg = soup_message_new_from_uri ("OPTIONS", star_uri);
- soup_session_send_message (session, msg);
-
- if (msg->status_code != SOUP_STATUS_NOT_FOUND) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- handled_by = soup_message_headers_get_one (msg->response_headers,
- "X-Handled-By");
- if (handled_by) {
- /* Should have been rejected by SoupServer directly */
- debug_printf (1, " Message reached handler '%s'\n",
- handled_by);
- errors++;
- }
- g_object_unref (msg);
-
- soup_server_add_handler (server, "*", server_star_callback, NULL, NULL);
-
- debug_printf (1, " Testing with handler\n");
- msg = soup_message_new_from_uri ("OPTIONS", star_uri);
- soup_session_send_message (session, msg);
-
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- handled_by = soup_message_headers_get_one (msg->response_headers,
- "X-Handled-By");
- if (!handled_by) {
- debug_printf (1, " Message did not reach handler!\n");
- errors++;
- } else if (strcmp (handled_by, "star_callback") != 0) {
- debug_printf (1, " Message reached incorrect handler '%s'\n",
- handled_by);
- errors++;
- }
- g_object_unref (msg);
-
- soup_test_session_abort_unref (session);
- soup_uri_free (star_uri);
-}
-
-/* Handle unexpectedly-early aborts. #596074, #618641 */
+/* Handle unexpectedly-early aborts. */
static void
ea_msg_completed_one (SoupSession *session, SoupMessage *msg, gpointer loop)
{
debug_printf (2, " Message 1 completed\n");
- if (msg->status_code != SOUP_STATUS_CANCELLED) {
- debug_printf (1, " Unexpected status on Message 1: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED);
g_main_loop_quit (loop);
}
@@ -585,7 +431,8 @@ do_early_abort_test (void)
GMainContext *context;
GMainLoop *loop;
- debug_printf (1, "\nAbort with pending connection (msg api)\n");
+ g_test_bug ("596074");
+ g_test_bug ("618641");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
msg = soup_message_new_from_uri ("GET", base_uri);
@@ -609,11 +456,7 @@ do_early_abort_test (void)
soup_session_send_message (session, msg);
debug_printf (2, " Message 2 completed\n");
- if (msg->status_code != SOUP_STATUS_CANCELLED) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED);
g_object_unref (msg);
while (g_main_context_pending (context))
@@ -621,6 +464,8 @@ do_early_abort_test (void)
soup_test_session_abort_unref (session);
+ g_test_bug ("668098");
+
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
msg = soup_message_new_from_uri ("GET", base_uri);
@@ -629,11 +474,7 @@ do_early_abort_test (void)
soup_session_send_message (session, msg);
debug_printf (2, " Message 3 completed\n");
- if (msg->status_code != SOUP_STATUS_CANCELLED) {
- debug_printf (1, " Unexpected response: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED);
g_object_unref (msg);
while (g_main_context_pending (context))
@@ -648,14 +489,8 @@ ear_one_completed (GObject *source, GAsyncResult *result, gpointer user_data)
GError *error = NULL;
debug_printf (2, " Request 1 completed\n");
- if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
- debug_printf (1, " Request 1 succeeded?\n");
- errors++;
- } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
- debug_printf (1, " Unexpected error on Request 1: %s\n",
- error->message);
- errors++;
- }
+ soup_request_send_finish (SOUP_REQUEST (source), result, &error);
+ g_assert_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED);
g_clear_error (&error);
}
@@ -665,14 +500,8 @@ ear_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
GError *error = NULL;
debug_printf (2, " Request 2 completed\n");
- if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
- debug_printf (1, " Request 2 succeeded?\n");
- errors++;
- } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
- debug_printf (1, " Unexpected error on Request 2: %s\n",
- error->message);
- errors++;
- }
+ soup_request_send_finish (SOUP_REQUEST (source), result, &error);
+ g_assert_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED);
g_clear_error (&error);
g_main_loop_quit (loop);
@@ -684,14 +513,8 @@ ear_three_completed (GObject *source, GAsyncResult *result, gpointer loop)
GError *error = NULL;
debug_printf (2, " Request 3 completed\n");
- if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
- debug_printf (1, " Request 3 succeeded?\n");
- errors++;
- } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- debug_printf (1, " Unexpected error on Request 3: %s\n",
- error->message);
- errors++;
- }
+ soup_request_send_finish (SOUP_REQUEST (source), result, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_clear_error (&error);
g_main_loop_quit (loop);
@@ -708,20 +531,15 @@ static void
do_early_abort_req_test (void)
{
SoupSession *session;
- SoupRequester *requester;
SoupRequest *req;
GMainContext *context;
GMainLoop *loop;
GCancellable *cancellable;
- debug_printf (1, "\nAbort with pending connection (request api)\n");
-
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- req = soup_requester_request_uri (requester, base_uri, NULL);
+ req = soup_session_request_uri (session, base_uri, NULL);
context = g_main_context_default ();
loop = g_main_loop_new (context, TRUE);
@@ -735,11 +553,9 @@ do_early_abort_req_test (void)
soup_test_session_abort_unref (session);
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- req = soup_requester_request_uri (requester, base_uri, NULL);
+ req = soup_session_request_uri (session, base_uri, NULL);
g_signal_connect (session, "connection-created",
G_CALLBACK (ea_connection_created), NULL);
@@ -753,11 +569,9 @@ do_early_abort_req_test (void)
soup_test_session_abort_unref (session);
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- req = soup_requester_request_uri (requester, base_uri, NULL);
+ req = soup_session_request_uri (session, base_uri, NULL);
cancellable = g_cancellable_new ();
g_signal_connect (session, "request-started",
@@ -790,21 +604,10 @@ do_one_accept_language_test (const char *language, const char *expected_header)
soup_session_send_message (session, msg);
soup_test_session_abort_unref (session);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " Message failed? %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
val = soup_message_headers_get_list (msg->request_headers,
"Accept-Language");
- if (!val) {
- debug_printf (1, " No Accept-Language set!\n");
- errors++;
- } else if (strcmp (val, expected_header) != 0) {
- debug_printf (1, " Wrong Accept-Language: expected '%s', got '%s'\n",
- expected_header, val);
- errors++;
- }
+ g_assert_cmpstr (val, ==, expected_header);
g_object_unref (msg);
}
@@ -814,7 +617,7 @@ do_accept_language_test (void)
{
const char *orig_language;
- debug_printf (1, "\nAutomatic Accept-Language processing\n");
+ g_test_bug ("602547");
orig_language = g_getenv ("LANGUAGE");
do_one_accept_language_test ("C", "en");
@@ -887,11 +690,7 @@ do_cancel_while_reading_test_for_session (SoupSession *session)
while (!done)
g_main_context_iteration (NULL, TRUE);
- if (msg->status_code != SOUP_STATUS_CANCELLED) {
- debug_printf (1, " FAILED: %d %s (expected Cancelled)\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED);
g_object_unref (msg);
if (thread)
@@ -903,7 +702,8 @@ do_cancel_while_reading_test (void)
{
SoupSession *session;
- debug_printf (1, "\nCancelling message while reading response (msg api)\n");
+ g_test_bug ("637741");
+ g_test_bug ("676038");
debug_printf (1, " Async session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -916,23 +716,9 @@ do_cancel_while_reading_test (void)
soup_test_session_abort_unref (session);
}
-static gboolean
-cancel_request_timeout (gpointer cancellable)
-{
- g_cancellable_cancel (cancellable);
- return FALSE;
-}
-
-static gpointer
-cancel_request_thread (gpointer cancellable)
-{
- g_usleep (100000); /* .1s */
- g_cancellable_cancel (cancellable);
- return NULL;
-}
-
static void
-do_cancel_while_reading_req_test_for_session (SoupRequester *requester)
+do_cancel_while_reading_req_test_for_session (SoupSession *session,
+ guint flags)
{
SoupRequest *req;
SoupURI *uri;
@@ -940,30 +726,12 @@ do_cancel_while_reading_req_test_for_session (SoupRequester *requester)
GError *error = NULL;
uri = soup_uri_new_with_base (base_uri, "/slow");
- req = soup_requester_request_uri (requester, uri, NULL);
+ req = soup_session_request_uri (session, uri, NULL);
soup_uri_free (uri);
cancellable = g_cancellable_new ();
-
- if (SOUP_IS_SESSION_ASYNC (soup_request_get_session (req))) {
- g_timeout_add (100, cancel_request_timeout, cancellable);
- soup_test_request_send_async_as_sync (req, cancellable, &error);
- } else {
- GThread *thread;
-
- thread = g_thread_new ("cancel_request_thread", cancel_request_thread, cancellable);
- soup_request_send (req, cancellable, &error);
- g_thread_unref (thread);
- }
-
- if (!error) {
- debug_printf (1, " Request succeeded?\n");
- errors++;
- } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- debug_printf (1, " Unexpected error: %s\n",
- error->message);
- errors++;
- }
+ soup_test_request_send (req, cancellable, flags, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_clear_error (&error);
g_object_unref (req);
@@ -971,169 +739,128 @@ do_cancel_while_reading_req_test_for_session (SoupRequester *requester)
}
static void
-do_cancel_while_reading_req_test (void)
+do_cancel_while_reading_immediate_req_test (void)
{
SoupSession *session;
- SoupRequester *requester;
+ guint flags;
- debug_printf (1, "\nCancelling message while reading response (request api)\n");
+ g_test_bug ("692310");
+
+ flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_IMMEDIATE;
debug_printf (1, " Async session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- do_cancel_while_reading_req_test_for_session (requester);
+ do_cancel_while_reading_req_test_for_session (session, flags);
soup_test_session_abort_unref (session);
debug_printf (1, " Sync session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
NULL);
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- do_cancel_while_reading_req_test_for_session (requester);
+ do_cancel_while_reading_req_test_for_session (session, flags);
soup_test_session_abort_unref (session);
}
static void
-do_aliases_test_for_session (SoupSession *session,
- const char *redirect_protocol)
+do_cancel_while_reading_delayed_req_test (void)
{
- SoupMessage *msg;
- SoupURI *uri;
- const char *redirected_protocol;
-
- uri = soup_uri_new_with_base (base_uri, "/alias-redirect");
- msg = soup_message_new_from_uri ("GET", uri);
- if (redirect_protocol)
- soup_message_headers_append (msg->request_headers, "X-Redirect-Protocol", redirect_protocol);
- soup_uri_free (uri);
- soup_session_send_message (session, msg);
+ SoupSession *session;
+ guint flags;
- redirected_protocol = soup_message_headers_get_one (msg->response_headers, "X-Redirected-Protocol");
+ flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_SOON;
- if (g_strcmp0 (redirect_protocol, redirected_protocol)) {
- debug_printf (1, " redirect went to %s, should have gone to %s!\n",
- redirected_protocol ? redirected_protocol : "(none)",
- redirect_protocol ? redirect_protocol : "(none)");
- errors++;
- } else if (redirect_protocol && !SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " msg failed? (%d %s)\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- } else if (!redirect_protocol && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " msg succeeded? (%d %s)\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ debug_printf (1, " Async session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ do_cancel_while_reading_req_test_for_session (session, flags);
+ soup_test_session_abort_unref (session);
- g_object_unref (msg);
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
+ NULL);
+ do_cancel_while_reading_req_test_for_session (session, flags);
+ soup_test_session_abort_unref (session);
}
static void
-do_aliases_test (void)
+do_cancel_while_reading_preemptive_req_test (void)
{
SoupSession *session;
- char *aliases[] = { "foo", NULL };
+ guint flags;
- debug_printf (1, "\nhttp-aliases / https-aliases\n");
+ g_test_bug ("637039");
- debug_printf (1, " Default behavior\n");
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- do_aliases_test_for_session (session, "http");
- soup_test_session_abort_unref (session);
+ flags = SOUP_TEST_REQUEST_CANCEL_CANCELLABLE | SOUP_TEST_REQUEST_CANCEL_PREEMPTIVE;
- debug_printf (1, " foo-means-https\n");
+ debug_printf (1, " Async session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_HTTPS_ALIASES, aliases,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
- do_aliases_test_for_session (session, "https");
+ do_cancel_while_reading_req_test_for_session (session, flags);
soup_test_session_abort_unref (session);
- debug_printf (1, " foo-means-nothing\n");
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_HTTP_ALIASES, NULL,
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
NULL);
- do_aliases_test_for_session (session, NULL);
+ do_cancel_while_reading_req_test_for_session (session, flags);
soup_test_session_abort_unref (session);
}
static void
-do_dot_dot_test (void)
+do_aliases_test_for_session (SoupSession *session,
+ const char *redirect_protocol)
{
- SoupSession *session;
SoupMessage *msg;
SoupURI *uri;
+ const char *redirected_protocol;
- debug_printf (1, "\n'..' smuggling test\n");
-
- 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 (base_uri, "/alias-redirect");
msg = soup_message_new_from_uri ("GET", uri);
+ if (redirect_protocol)
+ soup_message_headers_append (msg->request_headers, "X-Redirect-Protocol", redirect_protocol);
soup_uri_free (uri);
-
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_BAD_REQUEST) {
- debug_printf (1, " FAILED: %d %s (expected Bad Request)\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- g_object_unref (msg);
+ redirected_protocol = soup_message_headers_get_one (msg->response_headers, "X-Redirected-Protocol");
- soup_test_session_abort_unref (session);
+ g_assert_cmpstr (redirect_protocol, ==, redirected_protocol);
+ if (redirect_protocol)
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ else
+ soup_test_assert_message_status (msg, SOUP_STATUS_FOUND);
+
+ g_object_unref (msg);
}
static void
-do_ipv6_test (void)
+do_aliases_test (void)
{
- SoupServer *ipv6_server;
- SoupURI *ipv6_uri;
- SoupAddress *ipv6_addr;
SoupSession *session;
- SoupMessage *msg;
-
- debug_printf (1, "\nIPv6 server test\n");
-
- 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);
- soup_server_add_handler (ipv6_server, NULL, 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));
+ char *aliases[] = { "foo", NULL };
+ debug_printf (1, " Default behavior\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_aliases_test_for_session (session, "http");
+ soup_test_session_abort_unref (session);
- debug_printf (1, " HTTP/1.1\n");
- msg = soup_message_new_from_uri ("GET", ipv6_uri);
- soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " request failed: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- g_object_unref (msg);
-
- debug_printf (1, " HTTP/1.0\n");
- msg = soup_message_new_from_uri ("GET", ipv6_uri);
- soup_message_set_http_version (msg, SOUP_HTTP_1_0);
- soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " request failed: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
- g_object_unref (msg);
+ if (tls_available) {
+ debug_printf (1, " foo-means-https\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_HTTPS_ALIASES, aliases,
+ NULL);
+ do_aliases_test_for_session (session, "https");
+ soup_test_session_abort_unref (session);
+ } else
+ debug_printf (1, " foo-means-https -- SKIPPING\n");
- soup_uri_free (ipv6_uri);
+ debug_printf (1, " foo-means-nothing\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_HTTP_ALIASES, NULL,
+ NULL);
+ do_aliases_test_for_session (session, NULL);
soup_test_session_abort_unref (session);
- soup_test_server_quit_unref (ipv6_server);
}
static void
@@ -1143,7 +870,7 @@ do_idle_on_dispose_test (void)
SoupMessage *msg;
GMainContext *async_context;
- debug_printf (1, "\nTesting SoupSessionAsync dispose behavior\n");
+ g_test_bug ("667364");
async_context = g_main_context_new ();
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
@@ -1159,10 +886,8 @@ do_idle_on_dispose_test (void)
g_object_run_dispose (G_OBJECT (session));
- if (g_main_context_iteration (async_context, FALSE)) {
- debug_printf (1, " idle was queued!\n");
- errors++;
- }
+ if (g_main_context_iteration (async_context, FALSE))
+ soup_test_assert (FALSE, "idle was queued");
g_object_unref (session);
g_main_context_unref (async_context);
@@ -1175,7 +900,7 @@ do_pause_abort_test (void)
SoupMessage *msg;
gpointer ptr;
- debug_printf (1, "\nTesting paused messages don't get leaked on abort\n");
+ g_test_bug ("673905");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -1186,16 +911,14 @@ do_pause_abort_test (void)
g_object_add_weak_pointer (G_OBJECT (msg), &ptr);
soup_test_session_abort_unref (session);
- if (ptr) {
- debug_printf (1, " msg was leaked\n");
- errors++;
- }
+ g_assert_null (ptr);
}
int
main (int argc, char **argv)
{
SoupAuthDomain *auth_domain;
+ int ret;
test_init (argc, argv, NULL);
@@ -1212,32 +935,37 @@ main (int argc, char **argv)
soup_server_add_auth_domain (server, auth_domain);
g_object_unref (auth_domain);
- ssl_server = soup_test_server_new_ssl (TRUE);
- 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));
-
- do_host_test ();
- do_callback_unref_test ();
- do_callback_unref_req_test ();
- do_msg_reuse_test ();
- do_star_test ();
- do_early_abort_test ();
- do_early_abort_req_test ();
- do_accept_language_test ();
- do_cancel_while_reading_test ();
- do_cancel_while_reading_req_test ();
- do_aliases_test ();
- do_dot_dot_test ();
- do_ipv6_test ();
- do_idle_on_dispose_test ();
- do_pause_abort_test ();
+ if (tls_available) {
+ ssl_server = soup_test_server_new_ssl (TRUE);
+ 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));
+ }
+
+ 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);
+ g_test_add_func ("/misc/msg-reuse", do_msg_reuse_test);
+ g_test_add_func ("/misc/early-abort/msg", do_early_abort_test);
+ g_test_add_func ("/misc/early-abort/req", do_early_abort_req_test);
+ g_test_add_func ("/misc/accept-language", do_accept_language_test);
+ g_test_add_func ("/misc/cancel-while-reading/msg", do_cancel_while_reading_test);
+ g_test_add_func ("/misc/cancel-while-reading/req/immediate", do_cancel_while_reading_immediate_req_test);
+ g_test_add_func ("/misc/cancel-while-reading/req/delayed", do_cancel_while_reading_delayed_req_test);
+ g_test_add_func ("/misc/cancel-while-reading/req/preemptive", do_cancel_while_reading_preemptive_req_test);
+ 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);
+
+ ret = g_test_run ();
soup_uri_free (base_uri);
- soup_uri_free (ssl_base_uri);
soup_test_server_quit_unref (server);
- soup_test_server_quit_unref (ssl_server);
- test_cleanup ();
- return errors != 0;
+ if (tls_available) {
+ soup_uri_free (ssl_base_uri);
+ soup_test_server_quit_unref (ssl_server);
+ }
+
+ return ret;
}
diff --git a/tests/multipart-test.c b/tests/multipart-test.c
new file mode 100644
index 00000000..e057412e
--- /dev/null
+++ b/tests/multipart-test.c
@@ -0,0 +1,521 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "test-utils.h"
+
+#define READ_BUFFER_SIZE 8192
+
+typedef enum {
+ NO_MULTIPART,
+ SYNC_MULTIPART,
+ ASYNC_MULTIPART,
+ ASYNC_MULTIPART_SMALL_READS
+} MultipartMode;
+
+char *buffer;
+SoupSession *session;
+char *base_uri_string;
+SoupURI *base_uri;
+SoupMultipartInputStream *multipart;
+unsigned passes;
+
+
+/* This payload contains 4 different responses.
+ *
+ * First, a text/html response with a Content-Length (31);
+ * Second, a response lacking Content-Type with Content-Length (11);
+ * Third, a text/css response with no Content-Length;
+ * Fourth, same as the third, but with different content;
+ */
+const char *payload = \
+ "--cut-here\r\n" \
+ "Content-Type: text/html\n"
+ "Content-Length: 30\r\n" \
+ "\r\n" \
+ "<html><body>Hey!</body></html>" \
+ "\r\n--cut-here\r\n" \
+ "Content-Length: 10\r\n" \
+ "\r\n" \
+ "soup rocks" \
+ "\r\n--cut-here\r\n" \
+ "Content-Type: text/css\r\n" \
+ "\r\n" \
+ ".soup { before: rocks; }" \
+ "\r\n--cut-here\n" /* Tests boundary ending in a single \n. */ \
+ "Content-Type: text/css\r\n" \
+ "\r\n" \
+ "#soup { background-color: black; }" \
+ "\r\n--cut-here--";
+
+static void
+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_OK);
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "multipart/x-mixed-replace; boundary=cut-here");
+
+ soup_message_body_append (msg->response_body,
+ SOUP_MEMORY_STATIC,
+ payload,
+ strlen (payload));
+
+ soup_message_body_complete (msg->response_body);
+}
+
+static void
+content_sniffed (SoupMessage *msg, char *content_type, GHashTable *params, int *sniffed_count)
+{
+ *sniffed_count = *sniffed_count + 1;
+ debug_printf (2, " content-sniffed -> %s\n", content_type);
+}
+
+static void
+check_is_next (gboolean is_next)
+{
+ soup_test_assert (is_next,
+ "expected a header, but there are no more headers");
+}
+
+static void
+got_headers (SoupMessage *msg, int *headers_count)
+{
+ SoupMessageHeadersIter iter;
+ gboolean is_next;
+ const char* name, *value;
+
+ *headers_count = *headers_count + 1;
+
+ soup_message_headers_iter_init (&iter, msg->response_headers);
+
+ is_next = soup_message_headers_iter_next (&iter, &name, &value);
+ check_is_next (is_next);
+
+ if (g_str_equal (name, "Date")) {
+ is_next = soup_message_headers_iter_next (&iter, &name, &value);
+ check_is_next (is_next);
+ }
+
+ g_assert_cmpstr (name, ==, "Content-Type");
+ g_assert_cmpstr (value, ==, "multipart/x-mixed-replace; boundary=cut-here");
+}
+
+static void
+read_cb (GObject *source, GAsyncResult *asyncResult, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ GInputStream *stream = G_INPUT_STREAM (source);
+ GError *error = NULL;
+ gssize bytes_read;
+
+ bytes_read = g_input_stream_read_finish (stream, asyncResult, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_object_unref (stream);
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ if (!bytes_read) {
+ g_input_stream_close (stream, NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (stream);
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ g_input_stream_read_async (stream, buffer, READ_BUFFER_SIZE,
+ G_PRIORITY_DEFAULT, NULL,
+ read_cb, data);
+}
+
+static void
+no_multipart_handling_cb (GObject *source, GAsyncResult *res, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ SoupRequest *request = SOUP_REQUEST (source);
+ GError *error = NULL;
+ GInputStream* in;
+
+ in = soup_request_send_finish (request, res, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ g_input_stream_read_async (in, buffer, READ_BUFFER_SIZE,
+ G_PRIORITY_DEFAULT, NULL,
+ read_cb, data);
+}
+
+static void
+multipart_close_part_cb (GObject *source, GAsyncResult *res, gpointer data)
+{
+ GInputStream *in = G_INPUT_STREAM (source);
+ GError *error = NULL;
+
+ g_input_stream_close_finish (in, res, &error);
+ g_assert_no_error (error);
+}
+
+static void multipart_next_part_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer data);
+
+static void
+check_read (gsize nread, unsigned passes)
+{
+ switch (passes) {
+ case 0:
+ g_assert_cmpint (nread, ==, 30);
+ break;
+ case 1:
+ g_assert_cmpint (nread, ==, 10);
+ break;
+ case 2:
+ g_assert_cmpint (nread, ==, 24);
+ break;
+ case 3:
+ g_assert_cmpint (nread, ==, 34);
+ break;
+ default:
+ soup_test_assert (FALSE, "unexpected read of size: %d", (int)nread);
+ break;
+ }
+}
+
+static void
+multipart_read_cb (GObject *source, GAsyncResult *asyncResult, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ GInputStream *in = G_INPUT_STREAM (source);
+ GError *error = NULL;
+ static gssize bytes_read_for_part = 0;
+ gssize bytes_read;
+
+ bytes_read = g_input_stream_read_finish (in, asyncResult, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_input_stream_close_async (in, G_PRIORITY_DEFAULT, NULL,
+ multipart_close_part_cb, NULL);
+ g_object_unref (in);
+
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ /* Read 0 bytes - try to start reading another part. */
+ if (!bytes_read) {
+ check_read (bytes_read_for_part, passes);
+ bytes_read_for_part = 0;
+ passes++;
+
+ g_input_stream_close_async (in, G_PRIORITY_DEFAULT, NULL,
+ multipart_close_part_cb, NULL);
+ g_object_unref (in);
+
+ soup_multipart_input_stream_next_part_async (multipart, G_PRIORITY_DEFAULT, NULL,
+ multipart_next_part_cb, data);
+ return;
+ }
+
+ bytes_read_for_part += bytes_read;
+ g_input_stream_read_async (in, buffer, READ_BUFFER_SIZE,
+ G_PRIORITY_DEFAULT, NULL,
+ multipart_read_cb, data);
+}
+
+static void
+check_headers (SoupMultipartInputStream* multipart, unsigned passes)
+{
+ SoupMessageHeaders *headers;
+ SoupMessageHeadersIter iter;
+ gboolean is_next;
+ const char *name, *value;
+
+ headers = soup_multipart_input_stream_get_headers (multipart);
+ soup_message_headers_iter_init (&iter, headers);
+
+ switch (passes) {
+ case 0:
+ is_next = soup_message_headers_iter_next (&iter, &name, &value);
+ check_is_next (is_next);
+
+ g_assert_cmpstr (name, ==, "Content-Type");
+ g_assert_cmpstr (value, ==, "text/html");
+
+ is_next = soup_message_headers_iter_next (&iter, &name, &value);
+ check_is_next (is_next);
+
+ g_assert_cmpstr (name, ==, "Content-Length");
+ g_assert_cmpstr (value, ==, "30");
+
+ break;
+ case 1:
+ is_next = soup_message_headers_iter_next (&iter, &name, &value);
+ check_is_next (is_next);
+
+ g_assert_cmpstr (name, ==, "Content-Length");
+ g_assert_cmpstr (value, ==, "10");
+
+ break;
+ case 2:
+ case 3:
+ is_next = soup_message_headers_iter_next (&iter, &name, &value);
+ check_is_next (is_next);
+
+ g_assert_cmpstr (name, ==, "Content-Type");
+ g_assert_cmpstr (value, ==, "text/css");
+
+ break;
+ default:
+ soup_test_assert (FALSE, "unexpected part received");
+ break;
+ }
+}
+
+static void
+multipart_next_part_cb (GObject *source, GAsyncResult *res, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ GError *error = NULL;
+ GInputStream *in;
+ gsize read_size = READ_BUFFER_SIZE;
+
+ g_assert (SOUP_MULTIPART_INPUT_STREAM (source) == multipart);
+
+ in = soup_multipart_input_stream_next_part_finish (multipart, res, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_clear_error (&error);
+ g_object_unref (multipart);
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ if (!in) {
+ g_assert_cmpint (passes, ==, 4);
+ g_object_unref (multipart);
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ check_headers (multipart, passes);
+
+ if (g_object_get_data (G_OBJECT (multipart), "multipart-small-reads"))
+ read_size = 4;
+
+ g_input_stream_read_async (in, buffer, read_size,
+ G_PRIORITY_DEFAULT, NULL,
+ multipart_read_cb, data);
+}
+
+static void
+multipart_handling_cb (GObject *source, GAsyncResult *res, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ SoupRequest *request = SOUP_REQUEST (source);
+ GError *error = NULL;
+ GInputStream *in;
+ SoupMessage *message;
+
+ in = soup_request_send_finish (request, res, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+ multipart = soup_multipart_input_stream_new (message, in);
+ g_object_unref (message);
+ g_object_unref (in);
+
+ if (g_object_get_data (source, "multipart-small-reads"))
+ g_object_set_data (G_OBJECT (multipart), "multipart-small-reads", GINT_TO_POINTER(1));
+
+ soup_multipart_input_stream_next_part_async (multipart, G_PRIORITY_DEFAULT, NULL,
+ multipart_next_part_cb, data);
+}
+
+static void
+sync_multipart_handling_cb (GObject *source, GAsyncResult *res, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ SoupRequest *request = SOUP_REQUEST (source);
+ GError *error = NULL;
+ GInputStream *in;
+ SoupMessage *message;
+ char buffer[READ_BUFFER_SIZE];
+ gsize bytes_read;
+
+ in = soup_request_send_finish (request, res, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_main_loop_quit (loop);
+ return;
+ }
+
+ message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+ multipart = soup_multipart_input_stream_new (message, in);
+ g_object_unref (message);
+ g_object_unref (in);
+
+ while (TRUE) {
+ in = soup_multipart_input_stream_next_part (multipart, NULL, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_clear_error (&error);
+ break;
+ }
+
+ if (!in)
+ break;
+
+ check_headers (multipart, passes);
+
+ g_input_stream_read_all (in, (void*)buffer, sizeof (buffer), &bytes_read, NULL, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_clear_error (&error);
+ g_object_unref (in);
+ break;
+ }
+
+ check_read (bytes_read, passes);
+
+ passes++;
+ g_object_unref (in);
+ }
+
+ g_assert_cmpint (passes, ==, 4);
+
+ g_main_loop_quit (loop);
+ g_object_unref (multipart);
+}
+
+static void
+test_multipart (gconstpointer data)
+{
+ int headers_expected = 1, sniffed_expected = 1;
+ MultipartMode multipart_mode = GPOINTER_TO_INT (data);
+ SoupRequest* request;
+ SoupMessage *msg;
+ GMainLoop *loop;
+ int headers_count = 0;
+ int sniffed_count = 0;
+ GHashTable *params;
+ const char *content_type;
+ gboolean message_is_multipart = FALSE;
+ GError* error = NULL;
+
+ request = soup_session_request (session, base_uri_string, &error);
+ g_assert_no_error (error);
+ if (error)
+ return;
+
+ msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+
+ /* This is used to track the number of parts. */
+ passes = 0;
+
+ /* Force the server to close the connection. */
+ soup_message_headers_append (msg->request_headers,
+ "Connection", "close");
+
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (got_headers), &headers_count);
+
+ g_signal_connect (msg, "content-sniffed",
+ G_CALLBACK (content_sniffed), &sniffed_count);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ if (multipart_mode == ASYNC_MULTIPART)
+ soup_request_send_async (request, NULL, multipart_handling_cb, loop);
+ else if (multipart_mode == ASYNC_MULTIPART_SMALL_READS) {
+ g_object_set_data (G_OBJECT (request), "multipart-small-reads", GINT_TO_POINTER(1));
+ soup_request_send_async (request, NULL, multipart_handling_cb, loop);
+ } else if (multipart_mode == SYNC_MULTIPART)
+ soup_request_send_async (request, NULL, sync_multipart_handling_cb, loop);
+ else
+ soup_request_send_async (request, NULL, no_multipart_handling_cb, loop);
+
+ g_main_loop_run (loop);
+
+ content_type = soup_message_headers_get_content_type (msg->response_headers, &params);
+
+ if (content_type &&
+ g_str_has_prefix (content_type, "multipart/") &&
+ g_hash_table_lookup (params, "boundary")) {
+ message_is_multipart = TRUE;
+ }
+ g_clear_pointer (&params, g_hash_table_unref);
+
+ g_assert_true (message_is_multipart);
+ g_assert_cmpint (headers_count, ==, headers_expected);
+ g_assert_cmpint (sniffed_count, ==, sniffed_expected);
+
+ g_object_unref (msg);
+ g_object_unref (request);
+ g_main_loop_unref (loop);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ int ret;
+
+ test_init (argc, argv, NULL);
+
+ buffer = g_malloc (READ_BUFFER_SIZE);
+
+ server = soup_test_server_new (FALSE);
+ 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_string = soup_uri_to_string (base_uri, FALSE);
+
+ /* FIXME: I had to raise the number of connections allowed here, otherwise I
+ * was hitting the limit, which indicates some connections are not dying.
+ */
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ "use-thread-context", TRUE,
+ "max-conns", 20,
+ "max-conns-per-host", 20,
+ NULL);
+ soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
+
+ g_test_add_data_func ("/multipart/no", GINT_TO_POINTER (NO_MULTIPART), test_multipart);
+ g_test_add_data_func ("/multipart/sync", GINT_TO_POINTER (SYNC_MULTIPART), test_multipart);
+ g_test_add_data_func ("/multipart/async", GINT_TO_POINTER (ASYNC_MULTIPART), test_multipart);
+ g_test_add_data_func ("/multipart/async-small-reads", GINT_TO_POINTER (ASYNC_MULTIPART_SMALL_READS), test_multipart);
+
+ ret = g_test_run ();
+
+ soup_uri_free (base_uri);
+ g_free (base_uri_string);
+ g_free (buffer);
+
+ soup_test_session_abort_unref (session);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/no-ssl-test.c b/tests/no-ssl-test.c
new file mode 100644
index 00000000..82532c74
--- /dev/null
+++ b/tests/no-ssl-test.c
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include "test-utils.h"
+
+static void
+do_ssl_test_for_session (SoupSession *session, const char *uri)
+{
+ SoupMessage *msg;
+ GTlsCertificate *cert = NULL;
+ GTlsCertificateFlags flags;
+ gboolean is_https;
+
+ msg = soup_message_new ("GET", uri);
+ soup_session_send_message (session, msg);
+ soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED);
+
+ is_https = soup_message_get_https_status (msg, &cert, &flags);
+ soup_test_assert (!is_https, "get_http_status() returned TRUE? (flags %x)", flags);
+
+ g_assert_null (cert);
+ g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
+
+ g_object_unref (msg);
+}
+
+static void
+do_ssl_tests (gconstpointer uri)
+{
+ SoupSession *session;
+
+ g_test_bug ("700518");
+
+ debug_printf (1, " plain\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+ do_ssl_test_for_session (session, uri);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " async\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_ssl_test_for_session (session, uri);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " sync\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ do_ssl_test_for_session (session, uri);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_session_property_tests (void)
+{
+ gboolean use_system;
+ GTlsDatabase *tlsdb;
+ char *ca_file;
+ SoupSession *session;
+
+ g_test_bug ("700518");
+
+ session = soup_session_async_new ();
+
+ g_object_get (G_OBJECT (session),
+ "ssl-use-system-ca-file", &use_system,
+ "tls-database", &tlsdb,
+ "ssl-ca-file", &ca_file,
+ NULL);
+ soup_test_assert (!use_system, "ssl-use-system-ca-file defaults to TRUE");
+ soup_test_assert (tlsdb == NULL, "tls-database set by default");
+ soup_test_assert (ca_file == NULL, "ca-file set by default");
+
+ g_object_set (G_OBJECT (session),
+ "ssl-use-system-ca-file", TRUE,
+ NULL);
+ g_object_get (G_OBJECT (session),
+ "ssl-ca-file", &ca_file,
+ NULL);
+ soup_test_assert (ca_file == NULL, "setting ssl-use-system-ca-file set ssl-ca-file");
+
+ g_object_set (G_OBJECT (session),
+ "ssl-ca-file",
+ g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL),
+ NULL);
+ g_object_get (G_OBJECT (session),
+ "ssl-use-system-ca-file", &use_system,
+ "tls-database", &tlsdb,
+ "ssl-ca-file", &ca_file,
+ NULL);
+ soup_test_assert (ca_file == NULL, "setting ssl-ca-file did not fail");
+ soup_test_assert (!use_system, "setting ssl-ca-file set ssl-use-system-ca-file");
+ soup_test_assert (tlsdb == NULL, "setting ssl-ca-file set tls-database");
+
+ g_object_set (G_OBJECT (session),
+ "tls-database", NULL,
+ NULL);
+ g_object_get (G_OBJECT (session),
+ "ssl-use-system-ca-file", &use_system,
+ "tls-database", &tlsdb,
+ "ssl-ca-file", &ca_file,
+ NULL);
+ soup_test_assert (tlsdb == NULL, "setting tls-database NULL failed");
+ soup_test_assert (!use_system, "setting tls-database NULL set ssl-use-system-ca-file");
+ soup_test_assert (ca_file == NULL, "setting tls-database NULL set ssl-ca-file");
+
+ soup_test_session_abort_unref (session);
+}
+
+static void
+server_handler (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
+{
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "ok\r\n", 4);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ char *uri;
+ int ret;
+
+ /* Force this test to use the dummy TLS backend */
+ g_setenv ("GIO_USE_TLS", "dummy", TRUE);
+
+ test_init (argc, argv, NULL);
+
+ /* Make a non-SSL server and pretend that it's ssl, which is fine
+ * since we won't ever actually talk to it anyway. We don't
+ * currently test that failing to construct an SSL server works.
+ */
+ 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));
+
+ 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_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/ntlm-test-helper.c b/tests/ntlm-test-helper.c
new file mode 100644
index 00000000..f2450cae
--- /dev/null
+++ b/tests/ntlm-test-helper.c
@@ -0,0 +1,103 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include "libsoup/soup.h"
+
+const char *helper_protocol, *username, *domain;
+gboolean use_cached_creds;
+
+static GOptionEntry entries[] = {
+ { "helper-protocol", 0, 0,
+ G_OPTION_ARG_STRING, &helper_protocol,
+ NULL, NULL },
+ { "use-cached-creds", 0, 0,
+ G_OPTION_ARG_NONE, &use_cached_creds,
+ NULL, NULL },
+ { "username", 0, 0,
+ G_OPTION_ARG_STRING, &username,
+ NULL, NULL },
+ { "domain", 0, 0,
+ G_OPTION_ARG_STRING, &domain,
+ NULL, NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ GOptionContext *opts;
+ char buf[256], *header;
+ SoupMessage *msg;
+ SoupAuth *auth;
+
+ /* Don't recurse */
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE);
+
+ setlocale (LC_ALL, "");
+
+ opts = g_option_context_new (NULL);
+ g_option_context_add_main_entries (opts, entries, NULL);
+ if (!g_option_context_parse (opts, &argc, &argv, NULL)) {
+ g_printerr ("Bad arguments\n");
+ exit (1);
+ }
+ g_option_context_free (opts);
+
+ if (!username || !use_cached_creds || !helper_protocol ||
+ !g_str_equal (helper_protocol, "ntlmssp-client-1")) {
+ g_printerr ("Wrong arguments; this program is only intended for use by ntlm-test\n");
+ exit (1);
+ }
+
+ msg = soup_message_new ("GET", "http://localhost/");
+ auth = NULL;
+
+ while (fgets (buf, sizeof (buf), stdin)) {
+ if (strchr (buf, '\n'))
+ *strchr (buf, '\n') = '\0';
+ if (!strcmp (buf, "YR")) {
+ if (g_getenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS")) {
+ g_print ("PW\n");
+ continue;
+ }
+
+ g_clear_object (&auth);
+ auth = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL);
+ header = soup_auth_get_authorization (auth, msg);
+ g_print ("YR %s\n", header + 5);
+ g_free (header);
+ } else if (g_str_has_prefix (buf, "TT ")) {
+ header = g_strdup_printf ("NTLM %s\n", buf + 3);
+ if (!soup_auth_update (auth, msg, header)) {
+ g_printerr ("Bad challenge\n");
+ exit (1);
+ }
+ g_free (header);
+
+ soup_auth_authenticate (auth, username, "password");
+ header = soup_auth_get_authorization (auth, msg);
+ if (!header) {
+ g_printerr ("Internal authentication failure\n");
+ exit (1);
+ }
+ g_print ("KK %s\n", header + 5);
+ g_free (header);
+ } else {
+ g_printerr ("Unexpected command\n");
+ exit (1);
+ }
+ }
+
+ g_object_unref (msg);
+ g_clear_object (&auth);
+
+ return 0;
+}
diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c
index 00222e87..24a0f2e4 100644
--- a/tests/ntlm-test.c
+++ b/tests/ntlm-test.c
@@ -11,6 +11,8 @@
#include "test-utils.h"
+static SoupURI *uri;
+
typedef enum {
NTLM_UNAUTHENTICATED,
NTLM_RECEIVED_REQUEST,
@@ -19,12 +21,16 @@ typedef enum {
NTLM_AUTHENTICATED_BOB
} NTLMServerState;
+static const char *state_name[] = {
+ "unauth", "recv", "sent", "alice", "bob"
+};
+
#define NTLM_REQUEST_START "TlRMTVNTUAABAAAA"
#define NTLM_RESPONSE_START "TlRMTVNTUAADAAAA"
#define NTLM_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="
-#define NTLM_RESPONSE_USER(response) ((response)[102] == 'E' ? NTLM_AUTHENTICATED_ALICE : NTLM_AUTHENTICATED_BOB)
+#define NTLM_RESPONSE_USER(response) ((response)[86] == 'E' ? NTLM_AUTHENTICATED_ALICE : ((response)[86] == 'I' ? NTLM_AUTHENTICATED_BOB : NTLM_UNAUTHENTICATED))
static void
clear_state (gpointer connections, GObject *ex_connection)
@@ -134,16 +140,22 @@ 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);
}
+static gboolean authenticated_ntlm = FALSE;
+
static void
authenticate (SoupSession *session, SoupMessage *msg,
SoupAuth *auth, gboolean retrying, gpointer user)
{
- if (!retrying)
+ if (!retrying) {
soup_auth_authenticate (auth, user, "password");
+ if (g_str_equal (soup_auth_get_scheme_name (auth), "NTLM"))
+ authenticated_ntlm = TRUE;
+ }
}
typedef struct {
@@ -238,82 +250,65 @@ do_message (SoupSession *session, SoupURI *base_uri, const char *path,
if (state.got_ntlm_prompt) {
debug_printf (1, " NTLM_PROMPT");
- if (!get_ntlm_prompt) {
+ if (!get_ntlm_prompt)
debug_printf (1, "???");
- errors++;
- }
- } else if (get_ntlm_prompt) {
+ } else if (get_ntlm_prompt)
debug_printf (1, " no-ntlm-prompt???");
- errors++;
- }
if (state.got_basic_prompt) {
debug_printf (1, " BASIC_PROMPT");
- if (!get_basic_prompt) {
+ if (!get_basic_prompt)
debug_printf (1, "???");
- errors++;
- }
- } else if (get_basic_prompt) {
+ } else if (get_basic_prompt)
debug_printf (1, " no-basic-prompt???");
- errors++;
- }
if (state.sent_ntlm_request) {
debug_printf (1, " REQUEST");
- if (!do_ntlm) {
+ if (!do_ntlm)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_ntlm) {
+ } else if (do_ntlm)
debug_printf (1, " no-request???");
- errors++;
- }
if (state.got_ntlm_challenge) {
debug_printf (1, " CHALLENGE");
- if (!do_ntlm) {
+ if (!do_ntlm)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_ntlm) {
+ } else if (do_ntlm)
debug_printf (1, " no-challenge???");
- errors++;
- }
if (state.sent_ntlm_response) {
debug_printf (1, " NTLM_RESPONSE");
- if (!do_ntlm) {
+ if (!do_ntlm)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_ntlm) {
+ } else if (do_ntlm)
debug_printf (1, " no-ntlm-response???");
- errors++;
- }
if (state.sent_basic_response) {
debug_printf (1, " BASIC_RESPONSE");
- if (!do_basic) {
+ if (!do_basic)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_basic) {
+ } else if (do_basic)
debug_printf (1, " no-basic-response???");
- errors++;
- }
debug_printf (1, " -> %s", msg->reason_phrase);
- if (msg->status_code != status_code) {
+ if (msg->status_code != status_code)
debug_printf (1, "???");
- errors++;
- }
debug_printf (1, "\n");
+ g_assert_true (state.got_ntlm_prompt == get_ntlm_prompt);
+ g_assert_true (state.got_basic_prompt == get_basic_prompt);
+ g_assert_true (state.sent_ntlm_request == do_ntlm);
+ g_assert_true (state.got_ntlm_challenge == do_ntlm);
+ g_assert_true (state.sent_ntlm_response == do_ntlm);
+ g_assert_true (state.sent_basic_response == do_basic);
+ soup_test_assert_message_status (msg, status_code);
+
g_object_unref (msg);
}
static void
-do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
+do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
+ const char *user, gboolean use_builtin_ntlm)
{
SoupSession *session;
gboolean alice = !g_strcmp0 (user, "alice");
@@ -322,13 +317,23 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
gboolean bob_via_ntlm = use_ntlm && bob;
gboolean alice_via_basic = !use_ntlm && alice;
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- if (use_ntlm)
- soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
if (user) {
g_signal_connect (session, "authenticate",
G_CALLBACK (authenticate), (char *)user);
+ if (use_ntlm && !use_builtin_ntlm)
+ g_setenv ("NTLMUSER", user, TRUE);
+ }
+ if (use_ntlm) {
+ SoupAuthManager *auth_manager;
+ SoupAuth *ntlm;
+
+ soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+ auth_manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER));
+ ntlm = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL);
+ soup_auth_manager_use_auth (auth_manager, base_uri, ntlm);
+ g_object_unref (ntlm);
}
/* 1. Server doesn't request auth, so both get_ntlm_prompt and
@@ -336,11 +341,17 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
* if we're using NTLM we'll try that even without the server
* asking.
*/
+ authenticated_ntlm = FALSE;
do_message (session, base_uri, "/noauth",
FALSE, use_ntlm,
FALSE, FALSE,
SOUP_STATUS_OK);
+ soup_test_assert (authenticated_ntlm == (use_ntlm && use_builtin_ntlm),
+ "%s built-in NTLM support, but authenticate signal %s emitted\n",
+ use_builtin_ntlm ? "Using" : "Not using",
+ authenticated_ntlm ? "was" : "wasn't");
+
/* 2. Server requires auth as Alice, so it will request that
* if we didn't already authenticate the connection to her in
* the previous step. If we authenticated as Bob in the
@@ -421,46 +432,190 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
soup_test_session_abort_unref (session);
}
+typedef enum {
+ BUILTIN,
+ WINBIND,
+ FALLBACK
+} NtlmType;
+
+typedef struct {
+ const char *name, *user;
+ gboolean conn_uses_ntlm;
+ NtlmType ntlm_type;
+} NtlmTest;
+
+static const NtlmTest ntlm_tests[] = {
+ { "/ntlm/builtin/none", NULL, FALSE, BUILTIN },
+ { "/ntlm/builtin/alice", "alice", TRUE, BUILTIN },
+ { "/ntlm/builtin/bob", "bob", TRUE, BUILTIN },
+ { "/ntlm/builtin/basic", "alice", FALSE, BUILTIN },
+
+ { "/ntlm/winbind/none", NULL, FALSE, WINBIND },
+ { "/ntlm/winbind/alice", "alice", TRUE, WINBIND },
+ { "/ntlm/winbind/bob", "bob", TRUE, WINBIND },
+ { "/ntlm/winbind/basic", "alice", FALSE, WINBIND },
+
+ { "/ntlm/fallback/none", NULL, FALSE, FALLBACK },
+ { "/ntlm/fallback/alice", "alice", TRUE, FALLBACK },
+ { "/ntlm/fallback/bob", "bob", TRUE, FALLBACK },
+ { "/ntlm/fallback/basic", "alice", FALSE, FALLBACK }
+};
+
static void
-do_ntlm_tests (SoupURI *base_uri)
+do_ntlm_test (gconstpointer data)
{
- debug_printf (1, "Round 1: Non-NTLM Connection, no auth\n");
- do_ntlm_round (base_uri, FALSE, NULL);
- debug_printf (1, "Round 2: NTLM Connection, user=alice\n");
- do_ntlm_round (base_uri, TRUE, "alice");
- debug_printf (1, "Round 3: NTLM Connection, user=bob\n");
- do_ntlm_round (base_uri, TRUE, "bob");
- debug_printf (1, "Round 4: Non-NTLM Connection, user=alice\n");
- do_ntlm_round (base_uri, FALSE, "alice");
+ const NtlmTest *test = data;
+ gboolean use_builtin_ntlm = TRUE;
+
+ switch (test->ntlm_type) {
+ case BUILTIN:
+ /* Built-in NTLM auth support. (We set SOUP_NTLM_AUTH_DEBUG to
+ * an empty string to ensure that the built-in support is
+ * being used, even if /usr/bin/ntlm_auth is available.)
+ */
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE);
+ break;
+
+ case WINBIND:
+#ifndef USE_NTLM_AUTH
+ g_test_skip ("/usr/bin/ntlm_auth is not available");
+ return;
+#endif
+
+ /* Samba winbind /usr/bin/ntlm_auth helper support (via a
+ * helper program that emulates its interface).
+ */
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG",
+ g_test_get_filename (G_TEST_BUILT, "ntlm-test-helper", NULL),
+ TRUE);
+ g_unsetenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS");
+ use_builtin_ntlm = FALSE;
+ break;
+
+ case FALLBACK:
+#ifndef USE_NTLM_AUTH
+ g_test_skip ("/usr/bin/ntlm_auth is not available");
+ return;
+#endif
+
+ /* Support for when ntlm_auth is installed, but the user has
+ * no cached credentials (and thus we have to fall back to
+ * libsoup's built-in NTLM support).
+ */
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG",
+ g_test_get_filename (G_TEST_BUILT, "ntlm-test-helper", NULL),
+ TRUE);
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS", "1", TRUE);
+ break;
+ }
+
+ do_ntlm_round (uri, test->conn_uses_ntlm, test->user, use_builtin_ntlm);
+}
+
+static void
+retry_test_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying,
+ gpointer user_data)
+{
+ gboolean *retried = user_data;
+
+ if (!retrying) {
+ /* server_callback doesn't actually verify the password,
+ * only the username. So we pass an incorrect username
+ * rather than an incorrect password.
+ */
+ soup_auth_authenticate (auth, "wrong", "password");
+ } else if (!*retried) {
+ soup_auth_authenticate (auth, "alice", "password");
+ *retried = TRUE;
+ }
+}
+
+static void
+do_retrying_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *uri;
+ gboolean retried = FALSE;
+
+ g_test_bug ("693222");
+
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE);
+
+ debug_printf (1, " /alice\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM,
+ NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (retry_test_authenticate), &retried);
+
+ uri = soup_uri_new_with_base (base_uri, "/alice");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ soup_session_send_message (session, msg);
+
+ g_assert_true (retried);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " /bob\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM,
+ NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (retry_test_authenticate), &retried);
+ retried = FALSE;
+
+ uri = soup_uri_new_with_base (base_uri, "/bob");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ soup_session_send_message (session, msg);
+
+ g_assert_true (retried);
+ soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED);
+
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
}
int
main (int argc, char **argv)
{
- GMainLoop *loop;
SoupServer *server;
GHashTable *connections;
- SoupURI *uri;
+ int i, ret;
test_init (argc, argv, NULL);
- server = soup_test_server_new (FALSE);
+ server = soup_test_server_new (TRUE);
connections = g_hash_table_new (NULL, NULL);
soup_server_add_handler (server, NULL,
server_callback, connections, NULL);
- loop = g_main_loop_new (NULL, TRUE);
-
uri = soup_uri_new ("http://127.0.0.1/");
soup_uri_set_port (uri, soup_server_get_port (server));
- do_ntlm_tests (uri);
- soup_uri_free (uri);
- g_main_loop_unref (loop);
+ 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);
+
+ ret = g_test_run ();
+
+ soup_uri_free (uri);
soup_test_server_quit_unref (server);
test_cleanup ();
g_hash_table_destroy (connections);
- return errors != 0;
+ return ret;
}
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
index 85aac9f5..4b6679b3 100644
--- a/tests/proxy-test.c
+++ b/tests/proxy-test.c
@@ -6,16 +6,18 @@ typedef struct {
const char *explanation;
const char *url;
const guint final_status;
+ const char *bugref;
} SoupProxyTest;
static SoupProxyTest tests[] = {
- { "GET -> 200", "", SOUP_STATUS_OK },
- { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND },
- { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK },
- { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED },
- { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN },
+ { "GET -> 200", "", SOUP_STATUS_OK, NULL },
+ { "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND, NULL },
+ { "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK, NULL },
+ { "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED, NULL },
+ { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN, "577532" },
+ { "GET -> 200 (unproxied)", "http://localhost:47524/", SOUP_STATUS_OK, "700472" },
};
-static int ntests = sizeof (tests) / sizeof (tests[0]);
+static const int ntests = sizeof (tests) / sizeof (tests[0]);
#define HTTP_SERVER "http://127.0.0.1:47524"
#define HTTPS_SERVER "https://127.0.0.1:47525"
@@ -35,25 +37,23 @@ static const char *proxy_names[] = {
"authenticated proxy",
"unauthenticatable-to proxy"
};
+static GProxyResolver *proxy_resolvers[3];
+static const char *ignore_hosts[] = { "localhost", NULL };
static void
authenticate (SoupSession *session, SoupMessage *msg,
SoupAuth *auth, gboolean retrying, gpointer data)
{
if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
- if (soup_auth_is_for_proxy (auth)) {
- debug_printf (1, " got proxy auth object for 401!\n");
- errors++;
- }
+ soup_test_assert (!soup_auth_is_for_proxy (auth),
+ "got proxy auth object for 401");
} else if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
- if (!soup_auth_is_for_proxy (auth)) {
- debug_printf (1, " got regular auth object for 407!\n");
- errors++;
- }
+ soup_test_assert (soup_auth_is_for_proxy (auth),
+ "got regular auth object for 407");
} else {
- debug_printf (1, " got authenticate signal with status %d\n",
- msg->status_code);
- errors++;
+ soup_test_assert (FALSE,
+ "got authenticate signal with status %d\n",
+ msg->status_code);
}
if (!retrying)
@@ -80,28 +80,29 @@ test_url (const char *url, int proxy, guint expected,
gboolean sync, gboolean close)
{
SoupSession *session;
- SoupURI *proxy_uri;
SoupMessage *msg;
+ gboolean noproxy = !!strstr (url, "localhost");
if (!tls_available && g_str_has_prefix (url, "https:"))
return;
debug_printf (1, " GET %s via %s%s\n", url, proxy_names[proxy],
close ? " (with Connection: close)" : "");
- if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
+ if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN && !noproxy)
expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
/* We create a new session for each request to ensure that
* connections/auth aren't cached between tests.
*/
- proxy_uri = soup_uri_new (proxies[proxy]);
session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_PROXY_URI, proxy_uri,
+ SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[proxy],
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ SOUP_SESSION_SSL_STRICT, FALSE,
NULL);
- soup_uri_free (proxy_uri);
g_signal_connect (session, "authenticate",
G_CALLBACK (authenticate), NULL);
if (close) {
+ /* FIXME g_test_bug ("611663") */
g_signal_connect (session, "request-started",
G_CALLBACK (set_close_on_connect), NULL);
}
@@ -115,10 +116,7 @@ test_url (const char *url, int proxy, guint expected,
soup_session_send_message (session, msg);
debug_printf (1, " %d %s\n", msg->status_code, msg->reason_phrase);
- if (msg->status_code != expected) {
- debug_printf (1, " EXPECTED %d!\n", expected);
- errors++;
- }
+ soup_test_assert_message_status (msg, expected);
g_object_unref (msg);
soup_test_session_abort_unref (session);
@@ -129,73 +127,54 @@ test_url_new_api (const char *url, int proxy, guint expected,
gboolean sync, gboolean close)
{
SoupSession *session;
- SoupURI *proxy_uri;
SoupMessage *msg;
- SoupRequester *requester;
SoupRequest *request;
GInputStream *stream;
GError *error = NULL;
+ gboolean noproxy = !!strstr (url, "localhost");
+ /* FIXME g_test_skip() FIXME g_test_bug ("675865") */
if (!tls_available && g_str_has_prefix (url, "https:"))
return;
- debug_printf (1, " GET (requester API) %s via %s%s\n", url, proxy_names[proxy],
+ debug_printf (1, " GET (request API) %s via %s%s\n", url, proxy_names[proxy],
close ? " (with Connection: close)" : "");
- if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN)
+ if (proxy == UNAUTH_PROXY && expected != SOUP_STATUS_FORBIDDEN && !noproxy)
expected = SOUP_STATUS_PROXY_UNAUTHORIZED;
/* We create a new session for each request to ensure that
* connections/auth aren't cached between tests.
*/
- proxy_uri = soup_uri_new (proxies[proxy]);
session = soup_test_session_new (sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
- SOUP_SESSION_PROXY_URI, proxy_uri,
+ SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[proxy],
+ SOUP_SESSION_SSL_STRICT, FALSE,
NULL);
- soup_uri_free (proxy_uri);
g_signal_connect (session, "authenticate",
G_CALLBACK (authenticate), NULL);
if (close) {
+ /* FIXME g_test_bug ("611663") */
g_signal_connect (session, "request-started",
G_CALLBACK (set_close_on_connect), NULL);
}
- requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
- request = soup_requester_request (requester, url, NULL);
+ request = soup_session_request (session, url, NULL);
msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
- if (sync)
- stream = soup_request_send (request, NULL, &error);
- else
- stream = soup_test_request_send_async_as_sync (request, NULL, &error);
-
- if (!stream) {
- debug_printf (1, " Unexpected error on Request: %s\n",
- error->message);
- errors++;
- g_clear_error (&error);
- }
+ stream = soup_test_request_send (request, NULL, 0, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
if (stream) {
- if (sync)
- g_input_stream_close (stream, NULL, NULL);
- else
- soup_test_stream_close_async_as_sync (stream, NULL, NULL);
- if (error) {
- debug_printf (1, " Unexpected error on close: %s\n",
- error->message);
- errors++;
- g_clear_error (&error);
- }
+ 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);
- if (msg->status_code != expected) {
- debug_printf (1, " EXPECTED %d!\n", expected);
- errors++;
- }
+ soup_test_assert_message_status (msg, expected);
g_object_unref (msg);
g_object_unref (request);
@@ -204,42 +183,70 @@ test_url_new_api (const char *url, int proxy, guint expected,
}
static void
-run_test (int i, gboolean sync)
+do_proxy_test (SoupProxyTest *test, gboolean sync)
{
char *http_url, *https_url;
- debug_printf (1, "Test %d: %s (%s)\n", i + 1, tests[i].explanation,
- sync ? "sync" : "async");
+ if (test->bugref)
+ g_test_bug (test->bugref);
+
+ if (!strncmp (test->url, "http", 4)) {
+ SoupURI *uri;
+ guint port;
- if (!strncmp (tests[i].url, "http", 4)) {
- http_url = g_strdup (tests[i].url);
- https_url = g_strdup_printf ("https%s", tests[i].url + 4);
+ http_url = g_strdup (test->url);
+
+ uri = soup_uri_new (test->url);
+ port = uri->port;
+ soup_uri_set_scheme (uri, "https");
+ if (port)
+ soup_uri_set_port (uri, port + 1);
+ https_url = soup_uri_to_string (uri, FALSE);
+ soup_uri_free (uri);
} else {
- http_url = g_strconcat (HTTP_SERVER, tests[i].url, NULL);
- https_url = g_strconcat (HTTPS_SERVER, tests[i].url, NULL);
+ http_url = g_strconcat (HTTP_SERVER, test->url, NULL);
+ https_url = g_strconcat (HTTPS_SERVER, test->url, NULL);
}
- test_url (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
- test_url_new_api (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
- test_url (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
- test_url_new_api (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
+ test_url (http_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
+ test_url_new_api (http_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
+ test_url (https_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
+ test_url_new_api (https_url, SIMPLE_PROXY, test->final_status, sync, FALSE);
- test_url (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
- test_url_new_api (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
- test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
- test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
- test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
- test_url_new_api (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
+ test_url (http_url, AUTH_PROXY, test->final_status, sync, FALSE);
+ test_url_new_api (http_url, AUTH_PROXY, test->final_status, sync, FALSE);
+ test_url (https_url, AUTH_PROXY, test->final_status, sync, FALSE);
+ test_url_new_api (https_url, AUTH_PROXY, test->final_status, sync, FALSE);
+ test_url (https_url, AUTH_PROXY, test->final_status, sync, TRUE);
+ test_url_new_api (https_url, AUTH_PROXY, test->final_status, sync, TRUE);
- test_url (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
- test_url_new_api (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
- test_url (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
- test_url_new_api (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
+ test_url (http_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
+ test_url_new_api (http_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
+ test_url (https_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
+ test_url_new_api (https_url, UNAUTH_PROXY, test->final_status, sync, FALSE);
g_free (http_url);
g_free (https_url);
+}
+
+static void
+do_async_proxy_test (gconstpointer data)
+{
+ SoupProxyTest *test = (SoupProxyTest *)data;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
- debug_printf (1, "\n");
+ do_proxy_test (test, FALSE);
+}
+
+static void
+do_sync_proxy_test (gconstpointer data)
+{
+ SoupProxyTest *test = (SoupProxyTest *)data;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ do_proxy_test (test, TRUE);
}
static void
@@ -253,13 +260,14 @@ server_callback (SoupServer *server, SoupMessage *msg,
}
static void
-do_proxy_fragment_test (SoupURI *base_uri)
+do_proxy_fragment_test (gconstpointer data)
{
+ SoupURI *base_uri = (SoupURI *)data;
SoupSession *session;
SoupURI *proxy_uri, *req_uri;
SoupMessage *msg;
- debug_printf (1, "\nTesting request with fragment via proxy\n");
+ SOUP_TEST_SKIP_IF_NO_APACHE;
proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
@@ -272,11 +280,7 @@ do_proxy_fragment_test (SoupURI *base_uri)
soup_uri_free (req_uri);
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " unexpected status %d %s!\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_object_unref (msg);
soup_test_session_abort_unref (session);
@@ -289,7 +293,10 @@ do_proxy_redirect_test (void)
SoupURI *proxy_uri, *req_uri, *new_uri;
SoupMessage *msg;
- debug_printf (1, "\nTesting redirection through proxy\n");
+ g_test_bug ("631368");
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+ SOUP_TEST_SKIP_IF_NO_TLS;
proxy_uri = soup_uri_new (proxies[SIMPLE_PROXY]);
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
@@ -305,17 +312,11 @@ do_proxy_redirect_test (void)
soup_session_send_message (session, msg);
new_uri = soup_message_get_uri (msg);
- if (!strcmp (req_uri->path, new_uri->path)) {
- debug_printf (1, " message was not redirected!\n");
- errors++;
- }
+ soup_test_assert (strcmp (req_uri->path, new_uri->path) != 0,
+ "message was not redirected");
soup_uri_free (req_uri);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " unexpected status %d %s!\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_object_unref (msg);
soup_test_session_abort_unref (session);
@@ -326,14 +327,15 @@ main (int argc, char **argv)
{
SoupServer *server;
SoupURI *base_uri;
- int i;
+ char *path;
+ int i, ret;
test_init (argc, argv, NULL);
apache_init ();
- for (i = 0; i < ntests; i++) {
- run_test (i, FALSE);
- run_test (i, TRUE);
+ for (i = 0; i < 3; i++) {
+ proxy_resolvers[i] =
+ g_simple_proxy_resolver_new (proxies[i], (char **) ignore_hosts);
}
server = soup_test_server_new (TRUE);
@@ -341,12 +343,25 @@ main (int argc, char **argv)
base_uri = soup_uri_new ("http://127.0.0.1/");
soup_uri_set_port (base_uri, soup_server_get_port (server));
- do_proxy_fragment_test (base_uri);
- do_proxy_redirect_test ();
+ for (i = 0; i < ntests; i++) {
+ path = g_strdup_printf ("/proxy/async/%s", tests[i].explanation);
+ g_test_add_data_func (path, &tests[i], do_async_proxy_test);
+ g_free (path);
+ }
+ for (i = 0; i < ntests; i++) {
+ path = g_strdup_printf ("/proxy/sync/%s", tests[i].explanation);
+ g_test_add_data_func (path, &tests[i], do_sync_proxy_test);
+ g_free (path);
+ }
+
+ g_test_add_data_func ("/proxy/fragment", base_uri, do_proxy_fragment_test);
+ g_test_add_func ("/proxy/redirect", do_proxy_redirect_test);
+
+ ret = g_test_run ();
soup_uri_free (base_uri);
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/pull-api.c b/tests/pull-api.c
index 512d1768..2915b9e4 100644
--- a/tests/pull-api.c
+++ b/tests/pull-api.c
@@ -12,6 +12,7 @@ authenticate (SoupSession *session, SoupMessage *msg,
soup_auth_authenticate (auth, "user2", "realm2");
}
+#if HAVE_APACHE
static void
get_correct_response (const char *uri)
{
@@ -32,6 +33,7 @@ get_correct_response (const char *uri)
g_object_unref (msg);
soup_test_session_abort_unref (session);
}
+#endif
/* Pull API version 1: fully-async. More like a "poke" API. Rather
* than having SoupMessage emit "got_chunk" signals whenever it wants,
@@ -161,9 +163,7 @@ fully_async_got_headers (SoupMessage *msg, gpointer user_data)
*/
return;
} else if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " unexpected status: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
return;
}
@@ -192,18 +192,10 @@ fully_async_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
* test program, that means comparing it against
* correct_response to make sure that we got the right data.
*/
- if (ad->read_so_far + chunk->length > correct_response->length) {
- debug_printf (1, " read too far! (%lu > %lu)\n",
- (unsigned long) (ad->read_so_far + chunk->length),
- (unsigned long) correct_response->length);
- errors++;
- } else if (memcmp (chunk->data,
- correct_response->data + ad->read_so_far,
- chunk->length) != 0) {
- debug_printf (1, " data mismatch in block starting at %lu\n",
- (unsigned long) ad->read_so_far);
- errors++;
- }
+ g_assert_cmpint (ad->read_so_far + chunk->length, <=, correct_response->length);
+ soup_assert_cmpmem (chunk->data, chunk->length,
+ correct_response->data + ad->read_so_far,
+ chunk->length);
ad->read_so_far += chunk->length;
/* Now pause I/O, and prepare to read another chunk later.
@@ -224,11 +216,7 @@ fully_async_finished (SoupSession *session, SoupMessage *msg,
{
FullyAsyncData *ad = user_data;
- if (msg->status_code != ad->expected_status) {
- debug_printf (1, " unexpected final status: %d %s !\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, ad->expected_status);
if (ad->timeout != 0)
g_source_remove (ad->timeout);
@@ -240,6 +228,45 @@ fully_async_finished (SoupSession *session, SoupMessage *msg,
g_main_loop_quit (ad->loop);
}
+static void
+do_fast_async_test (gconstpointer data)
+{
+ const char *base_uri = data;
+ SoupSession *session;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
+ do_fully_async_test (session, base_uri, "/",
+ TRUE, SOUP_STATUS_OK);
+ do_fully_async_test (session, base_uri, "/Basic/realm1/",
+ TRUE, SOUP_STATUS_UNAUTHORIZED);
+ do_fully_async_test (session, base_uri, "/Basic/realm2/",
+ TRUE, SOUP_STATUS_OK);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_slow_async_test (gconstpointer data)
+{
+ const char *base_uri = data;
+ SoupSession *session;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (authenticate), NULL);
+ do_fully_async_test (session, base_uri, "/",
+ FALSE, SOUP_STATUS_OK);
+ do_fully_async_test (session, base_uri, "/Basic/realm1/",
+ FALSE, SOUP_STATUS_UNAUTHORIZED);
+ do_fully_async_test (session, base_uri, "/Basic/realm2/",
+ FALSE, SOUP_STATUS_OK);
+ soup_test_session_abort_unref (session);
+}
/* Pull API version 2: synchronous pull API via async I/O. */
@@ -284,14 +311,12 @@ do_synchronously_async_test (SoupSession *session,
/* Send the message, get back headers */
sync_async_send (session, msg);
- if (sync_async_is_finished (msg) &&
- expected_status == SOUP_STATUS_OK) {
- debug_printf (1, " finished without reading response!\n");
- errors++;
- } else if (!sync_async_is_finished (msg) &&
- expected_status != SOUP_STATUS_OK) {
- debug_printf (1, " request failed to fail!\n");
- errors++;
+ if (expected_status == SOUP_STATUS_OK) {
+ soup_test_assert (!sync_async_is_finished (msg),
+ "finished without reading response");
+ } else {
+ soup_test_assert (sync_async_is_finished (msg),
+ "request failed to fail");
}
/* Now we're ready to read the response body (though we could
@@ -303,32 +328,19 @@ do_synchronously_async_test (SoupSession *session,
(unsigned long) read_so_far,
(unsigned long) read_so_far + chunk->length);
- if (read_so_far + chunk->length > correct_response->length) {
- debug_printf (1, " read too far! (%lu > %lu)\n",
- (unsigned long) read_so_far + chunk->length,
- (unsigned long) correct_response->length);
- errors++;
- } else if (memcmp (chunk->data,
- correct_response->data + read_so_far,
- chunk->length) != 0) {
- debug_printf (1, " data mismatch in block starting at %lu\n",
- (unsigned long) read_so_far);
- errors++;
- }
+ g_assert_cmpint (read_so_far + chunk->length, <=, correct_response->length);
+ soup_assert_cmpmem (chunk->data, chunk->length,
+ correct_response->data + read_so_far,
+ chunk->length);
+
read_so_far += chunk->length;
soup_buffer_free (chunk);
}
- if (!sync_async_is_finished (msg) ||
- (msg->status_code == SOUP_STATUS_OK &&
- read_so_far != correct_response->length)) {
- debug_printf (1, " loop ended before message was fully read!\n");
- errors++;
- } else if (msg->status_code != expected_status) {
- debug_printf (1, " unexpected final status: %d %s !\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ g_assert_true (sync_async_is_finished (msg));
+ soup_test_assert_message_status (msg, expected_status);
+ if (msg->status_code == SOUP_STATUS_OK)
+ g_assert_cmpint (read_so_far, ==, correct_response->length);
sync_async_cleanup (msg);
g_object_unref (msg);
@@ -393,9 +405,7 @@ sync_async_got_headers (SoupMessage *msg, gpointer user_data)
*/
return;
} else if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " unexpected status: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
return;
}
@@ -473,44 +483,14 @@ sync_async_cleanup (SoupMessage *msg)
g_free (ad);
}
-
-int
-main (int argc, char **argv)
+static void
+do_sync_async_test (gconstpointer data)
{
+ const char *base_uri = data;
SoupSession *session;
- const char *base_uri;
- test_init (argc, argv, NULL);
- apache_init ();
-
- base_uri = "http://127.0.0.1:47524/";
- get_correct_response (base_uri);
-
- debug_printf (1, "\nFully async, fast requests\n");
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- g_signal_connect (session, "authenticate",
- G_CALLBACK (authenticate), NULL);
- do_fully_async_test (session, base_uri, "/",
- TRUE, SOUP_STATUS_OK);
- do_fully_async_test (session, base_uri, "/Basic/realm1/",
- TRUE, SOUP_STATUS_UNAUTHORIZED);
- do_fully_async_test (session, base_uri, "/Basic/realm2/",
- TRUE, SOUP_STATUS_OK);
- soup_test_session_abort_unref (session);
-
- debug_printf (1, "\nFully async, slow requests\n");
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- g_signal_connect (session, "authenticate",
- G_CALLBACK (authenticate), NULL);
- do_fully_async_test (session, base_uri, "/",
- FALSE, SOUP_STATUS_OK);
- do_fully_async_test (session, base_uri, "/Basic/realm1/",
- FALSE, SOUP_STATUS_UNAUTHORIZED);
- do_fully_async_test (session, base_uri, "/Basic/realm2/",
- FALSE, SOUP_STATUS_OK);
- soup_test_session_abort_unref (session);
+ SOUP_TEST_SKIP_IF_NO_APACHE;
- debug_printf (1, "\nSynchronously async\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
g_signal_connect (session, "authenticate",
G_CALLBACK (authenticate), NULL);
@@ -521,9 +501,33 @@ main (int argc, char **argv)
do_synchronously_async_test (session, base_uri, "/Basic/realm2/",
SOUP_STATUS_OK);
soup_test_session_abort_unref (session);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ const char *base_uri;
+ int ret;
+
+ test_init (argc, argv, NULL);
+ apache_init ();
+
+ base_uri = "http://127.0.0.1:47524/";
+#if HAVE_APACHE
+ get_correct_response (base_uri);
+#endif
+
+ g_test_add_data_func ("/pull-api/async/fast", base_uri, do_fast_async_test);
+ g_test_add_data_func ("/pull-api/async/slow", base_uri, do_slow_async_test);
+ g_test_add_data_func ("/pull-api/sync-async", base_uri, do_sync_async_test);
+
+ ret = g_test_run ();
+#if HAVE_APACHE
soup_buffer_free (correct_response);
+#endif
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/range-test.c b/tests/range-test.c
index 98f56450..c23ba462 100644
--- a/tests/range-test.c
+++ b/tests/range-test.c
@@ -7,23 +7,6 @@ int total_length;
char *test_response;
static void
-get_full_response (void)
-{
- char *contents;
- gsize length;
- GError *error = NULL;
-
- if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
- g_printerr ("Could not read index.txt: %s\n",
- error->message);
- exit (1);
- }
-
- full_response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
- debug_printf (1, "Total response length is %d\n\n", (int)length);
-}
-
-static void
check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
gboolean check_start_end, int expected_start, int expected_end)
{
@@ -33,41 +16,39 @@ check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
soup_message_headers_get_one (headers, "Content-Range"));
if (!soup_message_headers_get_content_range (headers, &start, &end, &total_length)) {
- debug_printf (1, " Could not find/parse Content-Range\n");
- errors++;
+ soup_test_assert (FALSE, "Could not find/parse Content-Range");
return;
}
if (total_length != full_response->length && total_length != -1) {
- debug_printf (1, " Unexpected total length %" G_GINT64_FORMAT " in response\n",
- total_length);
- errors++;
+ soup_test_assert (FALSE,
+ "Unexpected total length %" G_GINT64_FORMAT " in response\n",
+ total_length);
return;
}
if (check_start_end) {
if ((expected_start >= 0 && start != expected_start) ||
(expected_start < 0 && start != full_response->length + expected_start)) {
- debug_printf (1, " Unexpected range start %" G_GINT64_FORMAT " in response\n",
- start);
- errors++;
+ soup_test_assert (FALSE,
+ "Unexpected range start %" G_GINT64_FORMAT " in response\n",
+ start);
return;
}
if ((expected_end >= 0 && end != expected_end) ||
(expected_end < 0 && end != full_response->length - 1)) {
- debug_printf (1, " Unexpected range end %" G_GINT64_FORMAT " in response\n",
- end);
- errors++;
+ soup_test_assert (FALSE,
+ "Unexpected range end %" G_GINT64_FORMAT " in response\n",
+ end);
return;
}
}
if (end - start + 1 != body_len) {
- debug_printf (1, " Range length (%d) does not match body length (%d)\n",
- (int)(end - start) + 1,
- (int)body_len);
- errors++;
+ soup_test_assert (FALSE, "Range length (%d) does not match body length (%d)\n",
+ (int)(end - start) + 1,
+ (int)body_len);
return;
}
@@ -76,7 +57,7 @@ check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
static void
do_single_range (SoupSession *session, SoupMessage *msg,
- int start, int end)
+ int start, int end, gboolean succeed)
{
const char *content_type;
@@ -85,22 +66,26 @@ do_single_range (SoupSession *session, SoupMessage *msg,
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
- debug_printf (1, " Unexpected status %d %s\n",
- msg->status_code, msg->reason_phrase);
+ if (!succeed) {
+ soup_test_assert_message_status (msg, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
+ if (msg->status_code != SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
+ const char *content_range;
+
+ content_range = soup_message_headers_get_one (msg->response_headers,
+ "Content-Range");
+ if (content_range)
+ debug_printf (1, " Content-Range: %s\n", content_range);
+ }
+
g_object_unref (msg);
- errors++;
return;
}
+ soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
+
content_type = soup_message_headers_get_content_type (
msg->response_headers, NULL);
- if (content_type && !strcmp (content_type, "multipart/byteranges")) {
- debug_printf (1, " Response body should not have been multipart/byteranges\n");
- g_object_unref (msg);
- errors++;
- return;
- }
+ g_assert_cmpstr (content_type, !=, "multipart/byteranges");
check_part (msg->response_headers, msg->response_body->data,
msg->response_body->length, TRUE, start, end);
@@ -109,13 +94,13 @@ do_single_range (SoupSession *session, SoupMessage *msg,
static void
request_single_range (SoupSession *session, const char *uri,
- int start, int end)
+ int start, int end, gboolean succeed)
{
SoupMessage *msg;
msg = soup_message_new ("GET", uri);
soup_message_headers_set_range (msg->request_headers, start, end);
- do_single_range (session, msg, start, end);
+ do_single_range (session, msg, start, end, succeed);
}
static void
@@ -131,38 +116,21 @@ do_multi_range (SoupSession *session, SoupMessage *msg,
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
- debug_printf (1, " Unexpected status %d %s\n",
- msg->status_code, msg->reason_phrase);
- g_object_unref (msg);
- errors++;
- return;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
- if (!content_type || strcmp (content_type, "multipart/byteranges") != 0) {
- debug_printf (1, " Response Content-Type (%s) was not multipart/byteranges\n",
- content_type);
- g_object_unref (msg);
- errors++;
- return;
- }
+ g_assert_cmpstr (content_type, ==, "multipart/byteranges");
multipart = soup_multipart_new_from_message (msg->response_headers,
msg->response_body);
if (!multipart) {
- debug_printf (1, " Could not parse multipart\n");
+ soup_test_assert (FALSE, "Could not parse multipart");
g_object_unref (msg);
- errors++;
return;
}
length = soup_multipart_get_length (multipart);
- if (length != expected_return_ranges) {
- debug_printf (1, " Expected %d ranges, got %d\n",
- expected_return_ranges, length);
- errors++;
- }
+ g_assert_cmpint (length, ==, expected_return_ranges);
for (i = 0; i < length; i++) {
SoupMessageHeaders *headers;
@@ -196,7 +164,8 @@ request_double_range (SoupSession *session, const char *uri,
if (expected_return_ranges == 1) {
do_single_range (session, msg,
MIN (first_start, second_start),
- MAX (first_end, second_end));
+ MAX (first_end, second_end),
+ TRUE);
} else
do_multi_range (session, msg, expected_return_ranges);
}
@@ -223,13 +192,36 @@ request_triple_range (SoupSession *session, const char *uri,
if (expected_return_ranges == 1) {
do_single_range (session, msg,
MIN (first_start, MIN (second_start, third_start)),
- MAX (first_end, MAX (second_end, third_end)));
+ MAX (first_end, MAX (second_end, third_end)),
+ TRUE);
} else
do_multi_range (session, msg, expected_return_ranges);
}
static void
-do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
+request_semi_invalid_range (SoupSession *session, const char *uri,
+ int first_good_start, int first_good_end,
+ int bad_start, int bad_end,
+ int second_good_start, int second_good_end)
+{
+ SoupMessage *msg;
+ SoupRange ranges[3];
+
+ msg = soup_message_new ("GET", uri);
+ ranges[0].start = first_good_start;
+ ranges[0].end = first_good_end;
+ ranges[1].start = bad_start;
+ ranges[1].end = bad_end;
+ ranges[2].start = second_good_start;
+ ranges[2].end = second_good_end;
+ soup_message_headers_set_ranges (msg->request_headers, ranges, 3);
+
+ do_multi_range (session, msg, 2);
+}
+
+static void
+do_range_test (SoupSession *session, const char *uri,
+ gboolean expect_coalesce, gboolean expect_partial_coalesce)
{
int twelfths = full_response->length / 12;
@@ -255,7 +247,8 @@ do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
/* A: 0, simple request */
debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths);
request_single_range (session, uri,
- 0 * twelfths, 1 * twelfths);
+ 0 * twelfths, 1 * twelfths,
+ TRUE);
/* B: 11, end-relative request. These two are mostly redundant
* in terms of data coverage, but they may still catch
@@ -263,10 +256,12 @@ do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
*/
debug_printf (1, "Requesting %d-\n", 11 * twelfths);
request_single_range (session, uri,
- 11 * twelfths, -1);
+ 11 * twelfths, -1,
+ TRUE);
debug_printf (1, "Requesting -%d\n", 1 * twelfths);
request_single_range (session, uri,
- -1 * twelfths, -1);
+ -1 * twelfths, -1,
+ TRUE);
/* C: 2 and 5 */
debug_printf (1, "Requesting %d-%d,%d-%d\n",
@@ -309,12 +304,43 @@ do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
9 * twelfths, 10 * twelfths + 5,
4 * twelfths, 5 * twelfths,
10 * twelfths - 5, 11 * twelfths,
- expect_coalesce ? 2 : 3);
+ expect_partial_coalesce ? 2 : 3);
- if (memcmp (full_response->data, test_response, full_response->length) != 0) {
- debug_printf (1, "\nfull_response and test_response don't match\n");
- errors++;
- }
+ soup_assert_cmpmem (full_response->data, full_response->length,
+ test_response, full_response->length);
+
+ debug_printf (1, "Requesting (invalid) %d-%d\n",
+ (int) full_response->length + 1,
+ (int) full_response->length + 100);
+ request_single_range (session, uri,
+ full_response->length + 1, full_response->length + 100,
+ FALSE);
+
+ debug_printf (1, "Requesting (semi-invalid) 1-10,%d-%d,20-30\n",
+ (int) full_response->length + 1,
+ (int) full_response->length + 100);
+ request_semi_invalid_range (session, uri,
+ 1, 10,
+ full_response->length + 1, full_response->length + 100,
+ 20, 30);
+}
+
+static void
+do_apache_range_test (void)
+{
+ SoupSession *session;
+
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ 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);
}
static void
@@ -330,38 +356,44 @@ server_handler (SoupServer *server,
full_response);
}
-int
-main (int argc, char **argv)
+static void
+do_libsoup_range_test (void)
{
SoupSession *session;
SoupServer *server;
char *base_uri;
- test_init (argc, argv, NULL);
- apache_init ();
-
- get_full_response ();
- test_response = g_malloc0 (full_response->length);
-
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- debug_printf (1, "1. Testing against apache\n");
- do_range_test (session, "http://127.0.0.1:47524/", FALSE);
-
- debug_printf (1, "\n2. Testing against SoupServer\n");
server = soup_test_server_new (FALSE);
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);
+ do_range_test (session, base_uri, TRUE, TRUE);
g_free (base_uri);
soup_test_server_quit_unref (server);
soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ int ret;
+
+ test_init (argc, argv, NULL);
+ apache_init ();
+
+ full_response = soup_test_get_index ();
+ test_response = g_malloc0 (full_response->length);
+
+ g_test_add_func ("/ranges/apache", do_apache_range_test);
+ g_test_add_func ("/ranges/libsoup", do_libsoup_range_test);
+
+ ret = g_test_run ();
- soup_buffer_free (full_response);
g_free (test_response);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/redirect-test.c b/tests/redirect-test.c
index 59a2f077..ad8dabaa 100644
--- a/tests/redirect-test.c
+++ b/tests/redirect-test.c
@@ -5,7 +5,9 @@
#include "test-utils.h"
+SoupURI *base_uri;
char *server2_uri;
+SoupSession *async_session, *sync_session;
typedef struct {
const char *method;
@@ -14,104 +16,106 @@ typedef struct {
gboolean repeat;
} TestRequest;
-static struct {
+typedef struct {
TestRequest requests[3];
guint final_status;
- guint request_api_final_status;
-} tests[] = {
+ const char *bugref;
+} TestCase;
+
+static TestCase tests[] = {
/* A redirecty response to a GET or HEAD should cause a redirect */
{ { { "GET", "/301", 301 },
{ "GET", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, NULL },
{ { { "GET", "/302", 302 },
{ "GET", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, NULL },
{ { { "GET", "/303", 303 },
{ "GET", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, NULL },
{ { { "GET", "/307", 307 },
{ "GET", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, NULL },
{ { { "HEAD", "/301", 301 },
{ "HEAD", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, "551190" },
{ { { "HEAD", "/302", 302 },
{ "HEAD", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, "551190" },
/* 303 is a nonsensical response to HEAD, but some sites do
* it anyway. :-/
*/
{ { { "HEAD", "/303", 303 },
{ "HEAD", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, "600830" },
{ { { "HEAD", "/307", 307 },
{ "HEAD", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, "551190" },
/* A non-redirecty response to a GET or HEAD should not */
{ { { "GET", "/300", 300 },
- { NULL } }, 300 },
+ { NULL } }, 300, NULL },
{ { { "GET", "/304", 304 },
- { NULL } }, 304 },
+ { NULL } }, 304, NULL },
{ { { "GET", "/305", 305 },
- { NULL } }, 305 },
+ { NULL } }, 305, NULL },
{ { { "GET", "/306", 306 },
- { NULL } }, 306 },
+ { NULL } }, 306, NULL },
{ { { "GET", "/308", 308 },
- { NULL } }, 308 },
+ { NULL } }, 308, NULL },
{ { { "HEAD", "/300", 300 },
- { NULL } }, 300 },
+ { NULL } }, 300, "551190" },
{ { { "HEAD", "/304", 304 },
- { NULL } }, 304 },
+ { NULL } }, 304, "551190" },
{ { { "HEAD", "/305", 305 },
- { NULL } }, 305 },
+ { NULL } }, 305, "551190" },
{ { { "HEAD", "/306", 306 },
- { NULL } }, 306 },
+ { NULL } }, 306, "551190" },
{ { { "HEAD", "/308", 308 },
- { NULL } }, 308 },
+ { NULL } }, 308, "551190" },
/* Test double-redirect */
{ { { "GET", "/301/302", 301 },
{ "GET", "/302", 302 },
- { "GET", "/", 200 } }, 200 },
+ { "GET", "/", 200 } }, 200, NULL },
{ { { "HEAD", "/301/302", 301 },
{ "HEAD", "/302", 302 },
- { "HEAD", "/", 200 } }, 200 },
+ { "HEAD", "/", 200 } }, 200, "551190" },
/* POST should only automatically redirect on 301, 302 and 303 */
{ { { "POST", "/301", 301 },
{ "GET", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, "586692" },
{ { { "POST", "/302", 302 },
{ "GET", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, NULL },
{ { { "POST", "/303", 303 },
{ "GET", "/", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, NULL },
{ { { "POST", "/307", 307 },
- { NULL } }, 307 },
+ { NULL } }, 307, NULL },
/* Test behavior with recoverably-bad Location header */
{ { { "GET", "/bad", 302 },
{ "GET", "/bad%20with%20spaces", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, "566530" },
/* Test behavior with irrecoverably-bad Location header */
{ { { "GET", "/bad-no-host", 302 },
- { NULL } }, SOUP_STATUS_MALFORMED, 302 },
+ { NULL } }, SOUP_STATUS_MALFORMED, "528882" },
/* Test infinite redirection */
{ { { "GET", "/bad-recursive", 302, TRUE },
- { NULL } }, SOUP_STATUS_TOO_MANY_REDIRECTS },
+ { NULL } }, SOUP_STATUS_TOO_MANY_REDIRECTS, "604383" },
/* Test redirection to a different server */
{ { { "GET", "/server2", 302 },
{ "GET", "/on-server2", 200 },
- { NULL } }, 200 },
+ { NULL } }, 200, NULL },
};
static const int n_tests = G_N_ELEMENTS (tests);
@@ -131,11 +135,7 @@ got_headers (SoupMessage *msg, gpointer user_data)
if (!(*treq)->method)
return;
- if (msg->status_code != (*treq)->status_code) {
- debug_printf (1, " - Expected %d !\n",
- (*treq)->status_code);
- errors++;
- }
+ soup_test_assert_message_status (msg, (*treq)->status_code);
}
static void
@@ -149,35 +149,25 @@ restarted (SoupMessage *msg, gpointer user_data)
if ((*treq)->method && !(*treq)->repeat)
(*treq)++;
- if (!(*treq)->method) {
- debug_printf (1, " - Expected to be done!\n");
- errors++;
- return;
- }
+ soup_test_assert ((*treq)->method,
+ "Expected to be done");
- if (strcmp (msg->method, (*treq)->method) != 0) {
- debug_printf (1, " - Expected %s !\n", (*treq)->method);
- errors++;
- }
- if (strcmp (uri->path, (*treq)->path) != 0) {
- debug_printf (1, " - Expected %s !\n", (*treq)->path);
- errors++;
- }
+ g_assert_cmpstr (msg->method, ==, (*treq)->method);
+ g_assert_cmpstr (uri->path, ==, (*treq)->path);
}
static void
-do_message_api_test (SoupSession *session, SoupURI *base_uri, int n)
+do_message_api_test (SoupSession *session, TestCase *test)
{
SoupURI *uri;
SoupMessage *msg;
TestRequest *treq;
- debug_printf (1, "%2d. %s %s\n", n + 1,
- tests[n].requests[0].method,
- tests[n].requests[0].path);
+ if (test->bugref)
+ g_test_bug (test->bugref);
- uri = soup_uri_new_with_base (base_uri, tests[n].requests[0].path);
- msg = soup_message_new_from_uri (tests[n].requests[0].method, uri);
+ uri = soup_uri_new_with_base (base_uri, test->requests[0].path);
+ msg = soup_message_new_from_uri (test->requests[0].method, uri);
soup_uri_free (uri);
if (msg->method == SOUP_METHOD_POST) {
@@ -187,7 +177,7 @@ do_message_api_test (SoupSession *session, SoupURI *base_uri, int n)
strlen ("post body"));
}
- treq = &tests[n].requests[0];
+ treq = &test->requests[0];
g_signal_connect (msg, "got_headers",
G_CALLBACK (got_headers), &treq);
g_signal_connect (msg, "restarted",
@@ -195,53 +185,36 @@ do_message_api_test (SoupSession *session, SoupURI *base_uri, int n)
soup_session_send_message (session, msg);
- if (msg->status_code != tests[n].final_status) {
- debug_printf (1, " - Expected final status of %d, got %d !\n",
- tests[n].final_status, msg->status_code);
- errors++;
- }
+ soup_test_assert_message_status (msg, test->final_status);
g_object_unref (msg);
- debug_printf (2, "\n");
}
static void
-do_request_api_test (SoupSession *session, SoupURI *base_uri, int n)
+do_request_api_test (SoupSession *session, TestCase *test)
{
- SoupRequester *requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
SoupURI *uri;
- SoupRequest *req;
+ SoupRequestHTTP *reqh;
SoupMessage *msg;
TestRequest *treq;
GInputStream *stream;
GError *error = NULL;
- guint final_status;
-
- debug_printf (1, "%2d. %s %s\n", n + 1,
- tests[n].requests[0].method,
- tests[n].requests[0].path);
- final_status = tests[n].request_api_final_status;
- if (!final_status)
- final_status = tests[n].final_status;
+ if (test->bugref)
+ g_test_bug (test->bugref);
- uri = soup_uri_new_with_base (base_uri, tests[n].requests[0].path);
- req = soup_requester_request_uri (requester, uri, &error);
+ uri = soup_uri_new_with_base (base_uri, test->requests[0].path);
+ reqh = soup_session_request_http_uri (session,
+ test->requests[0].method,
+ uri, &error);
soup_uri_free (uri);
- if (!req) {
- debug_printf (1, " could not create request: %s\n",
- error->message);
+ g_assert_no_error (error);
+ if (error) {
g_error_free (error);
- errors++;
- debug_printf (2, "\n");
return;
}
- msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
- g_object_set (G_OBJECT (msg),
- SOUP_MESSAGE_METHOD, tests[n].requests[0].method,
- NULL);
-
+ msg = soup_request_http_get_message (reqh);
if (msg->method == SOUP_METHOD_POST) {
soup_message_set_request (msg, "text/plain",
SOUP_MEMORY_STATIC,
@@ -249,208 +222,75 @@ do_request_api_test (SoupSession *session, SoupURI *base_uri, int n)
strlen ("post body"));
}
- treq = &tests[n].requests[0];
+ treq = &test->requests[0];
g_signal_connect (msg, "got_headers",
G_CALLBACK (got_headers), &treq);
g_signal_connect (msg, "restarted",
G_CALLBACK (restarted), &treq);
- if (SOUP_IS_SESSION_SYNC (session))
- stream = soup_request_send (req, NULL, &error);
- else
- stream = soup_test_request_send_async_as_sync (req, NULL, &error);
-
- if (SOUP_STATUS_IS_TRANSPORT_ERROR (final_status)) {
- if (stream) {
- debug_printf (1, " expected failure (%s) but succeeded",
- soup_status_get_phrase (final_status));
- errors++;
- g_object_unref (stream);
- }
- if (error->domain != SOUP_HTTP_ERROR ||
- error->code != final_status) {
- debug_printf (1, " expected '%s' but got '%s'",
- soup_status_get_phrase (final_status),
- error->message);
- errors++;
- }
+ stream = soup_test_request_send (SOUP_REQUEST (reqh), NULL, 0, &error);
- g_error_free (error);
- g_object_unref (req);
- debug_printf (2, "\n");
- return;
- } else if (!stream) {
- debug_printf (1, " could not send request: %s\n",
- error->message);
- g_error_free (error);
- g_object_unref (req);
- errors++;
- debug_printf (2, "\n");
+ if (SOUP_STATUS_IS_TRANSPORT_ERROR (test->final_status) &&
+ test->final_status != SOUP_STATUS_MALFORMED) {
+ g_assert_error (error, SOUP_HTTP_ERROR, test->final_status);
+ g_clear_error (&error);
+
+ g_assert_null (stream);
+ g_clear_object (&stream);
+
+ g_object_unref (msg);
+ g_object_unref (reqh);
return;
}
- if (SOUP_IS_SESSION_SYNC (session))
- g_input_stream_close (stream, NULL, &error);
- else
- soup_test_stream_close_async_as_sync (stream, NULL, &error);
+ g_assert_no_error (error);
if (error) {
- debug_printf (1, " could not close stream: %s\n",
- error->message);
g_error_free (error);
- errors++;
+ g_object_unref (msg);
+ g_object_unref (reqh);
+ return;
}
- if (msg->status_code != final_status) {
- debug_printf (1, " - Expected final status of %d, got %d !\n",
- final_status, msg->status_code);
- errors++;
- }
+ soup_test_request_read_all (SOUP_REQUEST (reqh), stream, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
- g_object_unref (req);
- debug_printf (2, "\n");
-}
+ soup_test_request_close_stream (SOUP_REQUEST (reqh), stream, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ g_object_unref (stream);
-static void
-do_redirect_tests (SoupURI *base_uri)
-{
- SoupSession *session;
- int n;
-
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
- SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
- NULL);
- debug_printf (1, "Async session, SoupMessage\n");
- for (n = 0; n < n_tests; n++)
- do_message_api_test (session, base_uri, n);
- debug_printf (1, "\nAsync session, SoupRequest\n");
- for (n = 0; n < n_tests; n++)
- do_request_api_test (session, base_uri, n);
- soup_test_session_abort_unref (session);
-
- session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
- SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
- NULL);
- debug_printf (1, "\nSync session, SoupMessage\n");
- for (n = 0; n < n_tests; n++)
- do_message_api_test (session, base_uri, n);
- debug_printf (1, "\nSync session, SoupRequest\n");
- for (n = 0; n < n_tests; n++)
- do_request_api_test (session, base_uri, n);
- soup_test_session_abort_unref (session);
-}
-
-typedef struct {
- SoupSession *session;
- SoupMessage *msg1, *msg2;
- SoupURI *uri1, *uri2;
- SoupSocket *sock1, *sock2;
-} ConnectionTestData;
+ if (test->final_status == SOUP_STATUS_MALFORMED)
+ g_assert_cmpint (msg->status_code, ==, test->requests[0].status_code);
+ else
+ g_assert_cmpint (msg->status_code, ==, test->final_status);
-static void
-msg2_finished (SoupSession *session, SoupMessage *msg2, gpointer user_data)
-{
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg2->status_code)) {
- debug_printf (1, " msg2 failed: %d %s\n",
- msg2->status_code, msg2->reason_phrase);
- errors++;
- }
+ g_object_unref (msg);
+ g_object_unref (reqh);
}
static void
-unpause_msg1 (SoupMessage *msg2, gpointer user_data)
+do_async_msg_api_test (gconstpointer test)
{
- ConnectionTestData *data = user_data;
-
- if (!data->sock1) {
- debug_printf (1, " msg1 has no connection?\n");
- errors++;
- } else if (!data->sock2) {
- debug_printf (1, " msg2 has no connection?\n");
- errors++;
- } else if (data->sock1 == data->sock2) {
- debug_printf (1, " Both messages sharing the same connection\n");
- errors++;
- }
-
- soup_session_unpause_message (data->session, data->msg1);
-}
-
-static gboolean
-msg1_just_restarted (gpointer user_data)
-{
- ConnectionTestData *data = user_data;
-
- soup_session_pause_message (data->session, data->msg1);
-
- data->msg2 = soup_message_new_from_uri ("GET", data->uri2);
-
- g_signal_connect (data->msg2, "got_body",
- G_CALLBACK (unpause_msg1), data);
-
- soup_session_queue_message (data->session, data->msg2, msg2_finished, data);
- return FALSE;
+ do_message_api_test (async_session, (TestCase *)test);
}
static void
-msg1_about_to_restart (SoupMessage *msg1, gpointer user_data)
+do_async_req_api_test (gconstpointer test)
{
- ConnectionTestData *data = user_data;
-
- /* Do nothing when loading the redirected-to resource */
- if (!SOUP_STATUS_IS_REDIRECTION (data->msg1->status_code))
- return;
-
- /* We have to pause msg1 after the I/O finishes, but before
- * the queue runs again.
- */
- g_idle_add_full (G_PRIORITY_HIGH, msg1_just_restarted, data, NULL);
+ do_request_api_test (async_session, (TestCase *)test);
}
static void
-request_started (SoupSession *session, SoupMessage *msg,
- SoupSocket *socket, gpointer user_data)
+do_sync_msg_api_test (gconstpointer test)
{
- ConnectionTestData *data = user_data;
-
- if (msg == data->msg1)
- data->sock1 = socket;
- else if (msg == data->msg2)
- data->sock2 = socket;
- else
- g_warn_if_reached ();
+ do_message_api_test (sync_session, (TestCase *)test);
}
static void
-do_connection_test (SoupURI *base_uri)
+do_sync_req_api_test (gconstpointer test)
{
- ConnectionTestData data;
-
- debug_printf (1, "\nConnection reuse\n");
- memset (&data, 0, sizeof (data));
-
- data.session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- g_signal_connect (data.session, "request-started",
- G_CALLBACK (request_started), &data);
-
- data.uri1 = soup_uri_new_with_base (base_uri, "/301");
- data.uri2 = soup_uri_new_with_base (base_uri, "/");
- data.msg1 = soup_message_new_from_uri ("GET", data.uri1);
-
- g_signal_connect (data.msg1, "got-body",
- G_CALLBACK (msg1_about_to_restart), &data);
- soup_session_send_message (data.session, data.msg1);
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (data.msg1->status_code)) {
- debug_printf (1, " msg1 failed: %d %s\n",
- data.msg1->status_code, data.msg1->reason_phrase);
- errors++;
- }
- g_object_unref (data.msg1);
- soup_uri_free (data.uri1);
- soup_uri_free (data.uri2);
-
- soup_test_session_abort_unref (data.session);
+ do_request_api_test (sync_session, (TestCase *)test);
}
static void
@@ -550,29 +390,23 @@ server2_callback (SoupServer *server, SoupMessage *msg,
soup_message_set_status (msg, SOUP_STATUS_OK);
}
-static 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)
{
GMainLoop *loop;
SoupServer *server, *server2;
guint port;
- SoupURI *base_uri;
+ char *path;
+ int n, ret;
- test_init (argc, argv, no_test_entry);
+ test_init (argc, argv, NULL);
server = soup_test_server_new (TRUE);
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);
server2 = soup_test_server_new (TRUE);
soup_server_add_handler (server2, NULL,
@@ -582,23 +416,47 @@ main (int argc, char **argv)
loop = g_main_loop_new (NULL, TRUE);
- if (run_tests) {
- base_uri = soup_uri_new ("http://127.0.0.1");
- soup_uri_set_port (base_uri, port);
- do_redirect_tests (base_uri);
- do_connection_test (base_uri);
- soup_uri_free (base_uri);
- } else {
- g_print ("Listening on port %d\n", port);
- g_main_loop_run (loop);
+ async_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ 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,
+ tests[n].requests[0].status_code);
+ g_test_add_data_func (path, &tests[n], do_async_msg_api_test);
+ g_free (path);
+
+ path = g_strdup_printf ("/redirect/async/req/%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_req_api_test);
+ g_free (path);
+
+ path = g_strdup_printf ("/redirect/sync/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_sync_msg_api_test);
+ g_free (path);
+
+ path = g_strdup_printf ("/redirect/sync/req/%d-%s-%d", n,
+ tests[n].requests[0].method,
+ tests[n].requests[0].status_code);
+ g_test_add_data_func (path, &tests[n], do_sync_req_api_test);
+ g_free (path);
}
+ ret = g_test_run ();
+
g_main_loop_unref (loop);
- g_free (server2_uri);
+ soup_uri_free (base_uri);
soup_test_server_quit_unref (server);
+ g_free (server2_uri);
soup_test_server_quit_unref (server2);
- if (run_tests)
- test_cleanup ();
- return errors != 0;
+ soup_test_session_abort_unref (async_session);
+ soup_test_session_abort_unref (sync_session);
+
+ return ret;
}
diff --git a/tests/requester-test.c b/tests/requester-test.c
index b4e86ea6..39b30bd5 100644
--- a/tests/requester-test.c
+++ b/tests/requester-test.c
@@ -3,6 +3,9 @@
* Copyright (C) 2011 Red Hat, Inc.
*/
+/* Kill SoupRequester-related deprecation warnings */
+#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_40
+
#include "test-utils.h"
SoupServer *server;
@@ -14,24 +17,21 @@ 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"
-static void
-get_index (void)
+static gboolean
+slow_finish_message (gpointer msg)
{
- char *contents;
- gsize length;
- GError *error = NULL;
-
- if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
- g_printerr ("Could not read index.txt: %s\n",
- error->message);
- exit (1);
- }
+ SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
- response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
+ soup_server_unpause_message (server, msg);
+ return FALSE;
+}
- auth_response = soup_buffer_new (SOUP_MEMORY_STATIC,
- AUTH_HTML_BODY,
- strlen (AUTH_HTML_BODY));
+static void
+slow_pause_message (SoupMessage *msg, gpointer server)
+{
+ soup_server_pause_message (server, msg);
+ soup_add_timeout (soup_server_get_async_context (server),
+ 1000, slow_finish_message, msg);
}
static void
@@ -67,6 +67,10 @@ server_callback (SoupServer *server, SoupMessage *msg,
} else if (strcmp (path, "/non-persistent") == 0) {
soup_message_headers_append (msg->response_headers,
"Connection", "close");
+ } else if (!strcmp (path, "/slow")) {
+ g_object_set_data (G_OBJECT (msg), "server", server);
+ g_signal_connect (msg, "wrote-headers",
+ G_CALLBACK (slow_pause_message), server);
}
soup_message_set_status (msg, SOUP_STATUS_OK);
@@ -99,11 +103,8 @@ stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
GInputStream *stream = G_INPUT_STREAM (source);
GError *error = NULL;
- if (!g_input_stream_close_finish (stream, res, &error)) {
- debug_printf (1, " close failed: %s\n", error->message);
- g_error_free (error);
- errors++;
- }
+ g_input_stream_close_finish (stream, res, &error);
+ g_assert_no_error (error);
g_main_loop_quit (loop);
g_object_unref (stream);
}
@@ -119,9 +120,8 @@ test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data)
nread = g_input_stream_read_finish (stream, res, &error);
if (nread == -1) {
- debug_printf (1, " read_async failed: %s\n", error->message);
+ g_assert_no_error (error);
g_error_free (error);
- errors++;
g_input_stream_close (stream, NULL, NULL);
g_object_unref (stream);
g_main_loop_quit (loop);
@@ -150,28 +150,18 @@ auth_test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
if (!stream) {
- debug_printf (1, " send_async failed: %s\n", error->message);
- errors++;
+ g_assert_no_error (error);
+ g_clear_error (&error);
g_main_loop_quit (loop);
return;
}
msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source));
- if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) {
- debug_printf (1, " GET failed: %d %s\n", msg->status_code,
- msg->reason_phrase);
- errors++;
- g_main_loop_quit (loop);
- return;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED);
g_object_unref (msg);
content_type = soup_request_get_content_type (SOUP_REQUEST (source));
- if (g_strcmp0 (content_type, "text/html") != 0) {
- debug_printf (1, " failed to sniff Content-Type: got %s\n",
- content_type ? content_type : "(NULL)");
- errors++;
- }
+ g_assert_cmpstr (content_type, ==, "text/html");
g_input_stream_read_async (stream, buf, sizeof (buf),
G_PRIORITY_DEFAULT, NULL,
@@ -188,22 +178,13 @@ test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
if (data->cancel) {
- if (stream) {
- debug_printf (1, " send_async succeeded??\n");
- errors++;
- g_input_stream_close (stream, NULL, NULL);
- g_object_unref (stream);
- } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- debug_printf (1, " send_async failed with wrong error: %s\n", error->message);
- errors++;
- g_clear_error (&error);
- }
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
g_main_loop_quit (loop);
return;
} else {
+ g_assert_no_error (error);
if (!stream) {
- debug_printf (1, " send_async failed: %s\n", error->message);
- errors++;
g_main_loop_quit (loop);
g_clear_error (&error);
return;
@@ -211,11 +192,7 @@ test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
}
content_type = soup_request_get_content_type (SOUP_REQUEST (source));
- if (g_strcmp0 (content_type, "text/plain") != 0) {
- debug_printf (1, " failed to sniff Content-Type: got %s\n",
- content_type ? content_type : "(NULL)");
- errors++;
- }
+ g_assert_cmpstr (content_type, ==, "text/plain");
g_input_stream_read_async (stream, buf, sizeof (buf),
G_PRIORITY_DEFAULT, NULL,
@@ -234,6 +211,7 @@ request_started (SoupSession *session, SoupMessage *msg,
{
SoupSocket **save_socket = user_data;
+ g_clear_object (save_socket);
*save_socket = g_object_ref (socket);
}
@@ -250,11 +228,17 @@ do_async_test (SoupSession *session, SoupURI *uri,
SoupMessage *msg;
RequestData data;
- requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
+ if (SOUP_IS_SESSION_ASYNC (session))
+ requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
+ else
+ requester = NULL;
data.body = g_string_new (NULL);
data.cancel = cancel;
- request = soup_requester_request_uri (requester, uri, NULL);
+ 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) {
@@ -275,43 +259,20 @@ do_async_test (SoupSession *session, SoupURI *uri,
g_signal_handler_disconnect (session, started_id);
- if (msg->status_code != expected_status) {
- debug_printf (1, " GET failed: %d %s (expected %d)\n",
- msg->status_code, msg->reason_phrase,
- expected_status);
- g_object_unref (msg);
- errors++;
- return;
- }
+ soup_test_assert_message_status (msg, expected_status);
g_object_unref (msg);
- if (!expected_response) {
- if (data.body->len) {
- debug_printf (1, " body length mismatch: expected 0, got %d\n",
- (int)data.body->len);
- errors++;
- }
- } else if (data.body->len != expected_response->length) {
- debug_printf (1, " body length mismatch: expected %d, got %d\n",
- (int)expected_response->length, (int)data.body->len);
- errors++;
- } else if (memcmp (data.body->str, expected_response->data,
- expected_response->length) != 0) {
- debug_printf (1, " body data mismatch\n");
- errors++;
- }
+ if (expected_response) {
+ soup_assert_cmpmem (data.body->str, data.body->len,
+ expected_response->data, expected_response->length);
+ } else
+ g_assert_cmpint (data.body->len, ==, 0);
+
+ if (persistent)
+ g_assert_true (soup_socket_is_connected (socket));
+ else
+ g_assert_false (soup_socket_is_connected (socket));
- if (persistent) {
- if (!soup_socket_is_connected (socket)) {
- debug_printf (1, " socket not still connected!\n");
- errors++;
- }
- } else {
- if (soup_socket_is_connected (socket)) {
- debug_printf (1, " socket still connected!\n");
- errors++;
- }
- }
g_object_unref (socket);
g_string_free (data.body, TRUE);
@@ -323,19 +284,21 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
SoupRequester *requester;
SoupURI *uri;
- requester = soup_requester_new ();
- soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
- g_object_unref (requester);
+ if (SOUP_IS_SESSION_ASYNC (session)) {
+ requester = soup_requester_new ();
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
+ g_object_unref (requester);
+ }
soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
- debug_printf (1, " basic test\n");
+ debug_printf (1, " basic test\n");
uri = soup_uri_new (base_uri);
do_async_test (session, uri, test_sent,
SOUP_STATUS_OK, response,
TRUE, FALSE);
soup_uri_free (uri);
- debug_printf (1, " chunked test\n");
+ debug_printf (1, " chunked test\n");
uri = soup_uri_new (base_uri);
soup_uri_set_path (uri, "/chunked");
do_async_test (session, uri, test_sent,
@@ -343,7 +306,7 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
TRUE, FALSE);
soup_uri_free (uri);
- debug_printf (1, " auth test\n");
+ debug_printf (1, " auth test\n");
uri = soup_uri_new (base_uri);
soup_uri_set_path (uri, "/auth");
do_async_test (session, uri, auth_test_sent,
@@ -351,7 +314,7 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
TRUE, FALSE);
soup_uri_free (uri);
- debug_printf (1, " non-persistent test\n");
+ debug_printf (1, " non-persistent test\n");
uri = soup_uri_new (base_uri);
soup_uri_set_path (uri, "/non-persistent");
do_async_test (session, uri, test_sent,
@@ -359,7 +322,7 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
FALSE, FALSE);
soup_uri_free (uri);
- debug_printf (1, " cancellation test\n");
+ debug_printf (1, " cancellation test\n");
uri = soup_uri_new (base_uri);
soup_uri_set_path (uri, "/");
do_async_test (session, uri, test_sent,
@@ -369,11 +332,23 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
}
static void
-do_simple_test (const char *uri)
+do_simple_plain_test (gconstpointer uri)
+{
+ SoupSession *session;
+
+ g_test_bug ("653707");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+ do_test_for_thread_and_context (session, uri);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_simple_async_test (gconstpointer uri)
{
SoupSession *session;
- debug_printf (1, "Simple streaming test\n");
+ g_test_bug ("653707");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
@@ -382,16 +357,18 @@ do_simple_test (const char *uri)
soup_test_session_abort_unref (session);
}
-static gpointer
-do_test_with_context (const char *uri)
+static void
+do_test_with_context_and_type (const char *uri, gboolean plain_session)
{
GMainContext *async_context;
SoupSession *session;
+ g_test_bug ("653707");
+
async_context = g_main_context_new ();
g_main_context_push_thread_default (async_context);
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ session = soup_test_session_new (plain_session ? SOUP_TYPE_SESSION : SOUP_TYPE_SESSION_ASYNC,
SOUP_SESSION_ASYNC_CONTEXT, async_context,
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
@@ -401,25 +378,52 @@ do_test_with_context (const char *uri)
g_main_context_pop_thread_default (async_context);
g_main_context_unref (async_context);
- return NULL;
}
static void
-do_context_test (const char *uri)
+do_async_test_with_context (gconstpointer uri)
+{
+ do_test_with_context_and_type (uri, FALSE);
+}
+
+static void
+do_plain_test_with_context (gconstpointer uri)
+{
+ do_test_with_context_and_type (uri, TRUE);
+}
+
+static gpointer
+async_test_thread (gpointer uri)
{
- debug_printf (1, "Streaming with a non-default-context\n");
- do_test_with_context (uri);
+ do_test_with_context_and_type (uri, TRUE);
+ return NULL;
+}
+
+static gpointer
+plain_test_thread (gpointer uri)
+{
+ do_test_with_context_and_type (uri, FALSE);
+ return NULL;
}
static void
-do_thread_test (const char *uri)
+do_async_test_in_thread (gconstpointer uri)
{
GThread *thread;
- debug_printf (1, "Streaming in another thread\n");
+ thread = g_thread_new ("do_async_test_in_thread",
+ async_test_thread,
+ (gpointer)uri);
+ g_thread_join (thread);
+}
- thread = g_thread_new ("do_test_with_context",
- (GThreadFunc)do_test_with_context,
+static void
+do_plain_test_in_thread (gconstpointer uri)
+{
+ GThread *thread;
+
+ thread = g_thread_new ("do_plain_test_in_thread",
+ plain_test_thread,
(gpointer)uri);
g_thread_join (thread);
}
@@ -451,173 +455,381 @@ do_sync_request (SoupSession *session, SoupRequest *request,
in = soup_request_send (request, NULL, &error);
g_signal_handler_disconnect (session, started_id);
if (cancel) {
- if (in) {
- debug_printf (1, " send succeeded??\n");
- errors++;
- g_input_stream_close (in, NULL, NULL);
- g_object_unref (in);
- } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- debug_printf (1, " send failed with wrong error: %s\n", error->message);
- errors++;
- g_clear_error (&error);
- }
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
g_object_unref (msg);
+ g_object_unref (socket);
return;
} else if (!in) {
- debug_printf (1, " soup_request_send failed: %s\n",
- error->message);
- g_object_unref (msg);
+ g_assert_no_error (error);
g_clear_error (&error);
- errors++;
- return;
- }
-
- if (msg->status_code != expected_status) {
- debug_printf (1, " GET failed: %d %s\n", msg->status_code,
- msg->reason_phrase);
g_object_unref (msg);
- g_object_unref (in);
- errors++;
+ g_object_unref (socket);
return;
}
+
+ soup_test_assert_message_status (msg, expected_status);
g_object_unref (msg);
body = g_string_new (NULL);
do {
nread = g_input_stream_read (in, buf, sizeof (buf),
NULL, &error);
+ g_assert_no_error (error);
if (nread == -1) {
- debug_printf (1, " g_input_stream_read failed: %s\n",
- error->message);
g_clear_error (&error);
- errors++;
break;
}
g_string_append_len (body, buf, nread);
} while (nread > 0);
- if (!g_input_stream_close (in, NULL, &error)) {
- debug_printf (1, " g_input_stream_close failed: %s\n",
- error->message);
- g_clear_error (&error);
- errors++;
- }
+ g_input_stream_close (in, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
g_object_unref (in);
- if (!expected_response) {
- if (body->len) {
- debug_printf (1, " body length mismatch: expected 0, got %d\n",
- (int)body->len);
- errors++;
- }
- } else if (body->len != expected_response->length) {
- debug_printf (1, " body length mismatch: expected %d, got %d\n",
- (int)expected_response->length, (int)body->len);
- errors++;
- } else if (memcmp (body->str, expected_response->data, body->len) != 0) {
- debug_printf (1, " body data mismatch\n");
- errors++;
- }
+ if (expected_response) {
+ soup_assert_cmpmem (body->str, body->len,
+ expected_response->data, expected_response->length);
+ } else
+ g_assert_cmpint (body->len, ==, 0);
- if (persistent) {
- if (!soup_socket_is_connected (socket)) {
- debug_printf (1, " socket not still connected!\n");
- errors++;
- }
- } else {
- if (soup_socket_is_connected (socket)) {
- debug_printf (1, " socket still connected!\n");
- errors++;
- }
- }
+ if (persistent)
+ g_assert_true (soup_socket_is_connected (socket));
+ else
+ g_assert_false (soup_socket_is_connected (socket));
g_object_unref (socket);
g_string_free (body, TRUE);
}
static void
-do_sync_test (const char *uri_string)
+do_sync_tests_for_session (SoupSession *session, const char *uri_string)
{
- SoupSession *session;
SoupRequester *requester;
SoupRequest *request;
SoupURI *uri;
- debug_printf (1, "Sync streaming\n");
-
- session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
- requester = soup_requester_new ();
- soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
- g_object_unref (requester);
+ requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
uri = soup_uri_new (uri_string);
- debug_printf (1, " basic test\n");
- request = soup_requester_request_uri (requester, uri, NULL);
+ debug_printf (1, " basic test\n");
+ if (requester)
+ request = soup_requester_request_uri (requester, uri, NULL);
+ else
+ request = soup_session_request_uri (session, uri, NULL);
do_sync_request (session, request,
SOUP_STATUS_OK, response,
TRUE, FALSE);
g_object_unref (request);
- debug_printf (1, " chunked test\n");
+ debug_printf (1, " chunked test\n");
soup_uri_set_path (uri, "/chunked");
- request = soup_requester_request_uri (requester, uri, NULL);
+ if (requester)
+ request = soup_requester_request_uri (requester, uri, NULL);
+ else
+ request = soup_session_request_uri (session, uri, NULL);
do_sync_request (session, request,
SOUP_STATUS_OK, response,
TRUE, FALSE);
g_object_unref (request);
- debug_printf (1, " auth test\n");
+ debug_printf (1, " auth test\n");
soup_uri_set_path (uri, "/auth");
- request = soup_requester_request_uri (requester, uri, NULL);
+ if (requester)
+ request = soup_requester_request_uri (requester, uri, NULL);
+ else
+ request = soup_session_request_uri (session, uri, NULL);
do_sync_request (session, request,
SOUP_STATUS_UNAUTHORIZED, auth_response,
TRUE, FALSE);
g_object_unref (request);
- debug_printf (1, " non-persistent test\n");
+ debug_printf (1, " non-persistent test\n");
soup_uri_set_path (uri, "/non-persistent");
- request = soup_requester_request_uri (requester, uri, NULL);
+ if (requester)
+ request = soup_requester_request_uri (requester, uri, NULL);
+ else
+ request = soup_session_request_uri (session, uri, NULL);
do_sync_request (session, request,
SOUP_STATUS_OK, response,
FALSE, FALSE);
g_object_unref (request);
- debug_printf (1, " cancel test\n");
+ debug_printf (1, " cancel test\n");
soup_uri_set_path (uri, "/");
- request = soup_requester_request_uri (requester, uri, NULL);
+ if (requester)
+ request = soup_requester_request_uri (requester, uri, NULL);
+ else
+ request = soup_session_request_uri (session, uri, NULL);
do_sync_request (session, request,
SOUP_STATUS_FORBIDDEN, NULL,
TRUE, TRUE);
g_object_unref (request);
+ soup_uri_free (uri);
+}
+
+static void
+do_plain_sync_test (gconstpointer uri)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+ do_sync_tests_for_session (session, uri);
soup_test_session_abort_unref (session);
+}
+
+static void
+do_sync_sync_test (gconstpointer uri)
+{
+ SoupSession *session;
+ SoupRequester *requester;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ requester = soup_requester_new ();
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
+ g_object_unref (requester);
+ do_sync_tests_for_session (session, uri);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_null_char_request (SoupSession *session, const char *encoded_data,
+ const char *expected_data, int expected_len)
+{
+ GError *error = NULL;
+ GInputStream *stream;
+ SoupRequest *request;
+ SoupURI *uri;
+ char *uri_string, buf[256];
+ gsize nread;
+
+ uri_string = g_strdup_printf ("data:text/html,%s", encoded_data);
+ uri = soup_uri_new (uri_string);
+ g_free (uri_string);
+
+ request = soup_session_request_uri (session, uri, NULL);
+ stream = soup_test_request_send (request, NULL, 0, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_error_free (error);
+ g_object_unref (request);
+ soup_uri_free (uri);
+ return;
+ }
+
+ g_input_stream_read_all (stream, buf, sizeof (buf), &nread, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+
+ soup_test_request_close_stream (request, stream, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+
+ soup_assert_cmpmem (buf, nread, expected_data, expected_len);
+
+ g_object_unref (stream);
+ g_object_unref (request);
soup_uri_free (uri);
}
+static void
+do_null_char_test_for_session (SoupSession *session)
+{
+ static struct {
+ const char *encoded_data;
+ const char *expected_data;
+ int expected_len;
+ } test_cases[] = {
+ { "%3Cscript%3Ea%3D'%00'%3C%2Fscript%3E", "<script>a='\0'</script>", 22 },
+ { "%00%3Cscript%3Ea%3D42%3C%2Fscript%3E", "\0<script>a=42</script>", 22 },
+ { "%3Cscript%3E%00%3Cbr%2F%3E%3C%2Fscript%3E%00", "<script>\0<br/></script>\0", 24 },
+ };
+ static int num_test_cases = G_N_ELEMENTS(test_cases);
+ int i;
+
+ for (i = 0; i < num_test_cases; i++) {
+ do_null_char_request (session, test_cases[i].encoded_data,
+ test_cases[i].expected_data, test_cases[i].expected_len);
+ }
+}
+
+static void
+do_plain_null_char_test (void)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+ do_null_char_test_for_session (session);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_async_null_char_test (void)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ do_null_char_test_for_session (session);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+close_test_msg_finished (SoupMessage *msg,
+ gpointer user_data)
+{
+ gboolean *finished = user_data;
+
+ *finished = TRUE;
+}
+
+static void
+do_close_test_for_session (SoupSession *session,
+ SoupURI *uri)
+{
+ GError *error = NULL;
+ GInputStream *stream;
+ SoupRequest *request;
+ guint64 start, end;
+ GCancellable *cancellable;
+ SoupMessage *msg;
+ gboolean finished = FALSE;
+
+ debug_printf (1, " normal close\n");
+
+ request = soup_session_request_uri (session, uri, NULL);
+ stream = soup_test_request_send (request, NULL, 0, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_error_free (error);
+ g_object_unref (request);
+ return;
+ }
+
+ start = g_get_monotonic_time ();
+ soup_test_request_close_stream (request, stream, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ end = g_get_monotonic_time ();
+
+ g_assert_cmpint (end - start, <=, 500000);
+
+ g_object_unref (stream);
+ g_object_unref (request);
+
+
+ debug_printf (1, " error close\n");
+
+ request = soup_session_request_uri (session, uri, NULL);
+ msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+ g_signal_connect (msg, "finished", G_CALLBACK (close_test_msg_finished), &finished);
+ g_object_unref (msg);
+
+ stream = soup_test_request_send (request, NULL, 0, &error);
+ g_assert_no_error (error);
+ if (error) {
+ g_error_free (error);
+ g_object_unref (request);
+ return;
+ }
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ soup_test_request_close_stream (request, stream, cancellable, &error);
+ if (error)
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
+
+ g_assert_true (finished);
+
+ g_object_unref (stream);
+ g_object_unref (request);
+}
+
+static void
+do_async_close_test (gconstpointer uri)
+{
+ 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");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ do_close_test_for_session (session, slow_uri);
+ soup_test_session_abort_unref (session);
+
+ soup_uri_free (slow_uri);
+}
+
+static void
+do_sync_close_test (gconstpointer uri)
+{
+ 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");
+
+ debug_printf (1, " SoupSessionSync\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ do_close_test_for_session (session, slow_uri);
+ soup_test_session_abort_unref (session);
+
+ soup_uri_free (slow_uri);
+}
+
int
main (int argc, char **argv)
{
char *uri;
+ int ret;
test_init (argc, argv, NULL);
- get_index ();
+
+ response = soup_test_get_index ();
+ auth_response = soup_buffer_new (SOUP_MEMORY_STATIC,
+ AUTH_HTML_BODY,
+ strlen (AUTH_HTML_BODY));
server = soup_test_server_new (TRUE);
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));
- do_simple_test (uri);
- do_thread_test (uri);
- do_context_test (uri);
- do_sync_test (uri);
+ 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);
+ g_test_add_data_func ("/requester/threaded/SoupSession", uri, do_plain_test_in_thread);
+ g_test_add_data_func ("/requester/threaded/SoupSessionAsync", uri, do_async_test_in_thread);
+ g_test_add_data_func ("/requester/context/SoupSession", uri, do_plain_test_with_context);
+ g_test_add_data_func ("/requester/context/SoupSessionAsync", uri, do_async_test_with_context);
+ g_test_add_data_func ("/requester/sync/SoupSession", uri, do_plain_sync_test);
+ g_test_add_data_func ("/requester/sync/SoupSessionSync", uri, do_sync_sync_test);
+ g_test_add_func ("/requester/null-char/SoupSession", do_plain_null_char_test);
+ g_test_add_func ("/requester/null-char/SoupSessionAsync", do_async_null_char_test);
+ g_test_add_data_func ("/requester/close/SoupSessionAsync", uri, do_async_close_test);
+ g_test_add_data_func ("/requester/close/SoupSessionSync", uri, do_sync_close_test);
+
+ ret = g_test_run ();
g_free (uri);
- soup_buffer_free (response);
soup_buffer_free (auth_response);
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/resource-test.c b/tests/resource-test.c
new file mode 100644
index 00000000..6fcb899e
--- /dev/null
+++ b/tests/resource-test.c
@@ -0,0 +1,220 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ */
+
+#include "test-utils.h"
+
+SoupBuffer *index_buffer;
+
+typedef struct {
+ GString *body;
+ char buffer[1024];
+ GMainLoop *loop;
+} AsyncRequestData;
+
+static void
+stream_closed (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GInputStream *in = G_INPUT_STREAM (source);
+ AsyncRequestData *data = user_data;
+ GError *error = NULL;
+
+ g_input_stream_close_finish (in, result, &error);
+ g_assert_no_error (error);
+ g_main_loop_quit (data->loop);
+ g_object_unref (in);
+}
+
+static void
+test_read_ready (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GInputStream *in = G_INPUT_STREAM (source);
+ AsyncRequestData *data = user_data;
+ gssize nread;
+ GError *error = NULL;
+
+ nread = g_input_stream_read_finish (in, result, &error);
+ if (nread == -1) {
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ g_input_stream_close (in, NULL, NULL);
+ g_object_unref (in);
+ return;
+ } else if (nread == 0) {
+ g_input_stream_close_async (in, G_PRIORITY_DEFAULT, NULL,
+ stream_closed, data);
+ return;
+ }
+
+ g_string_append_len (data->body, data->buffer, nread);
+ g_input_stream_read_async (in, data->buffer, sizeof (data->buffer),
+ G_PRIORITY_DEFAULT, NULL,
+ test_read_ready, data);
+}
+
+static void
+async_request_sent (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GInputStream *in;
+ AsyncRequestData *data = user_data;
+ GError *error = NULL;
+
+ in = soup_request_send_finish (SOUP_REQUEST (source), result, &error);
+ if (!in) {
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ return;
+ }
+
+ g_input_stream_read_async (in, data->buffer, sizeof (data->buffer),
+ G_PRIORITY_DEFAULT, NULL,
+ test_read_ready, data);
+}
+
+static void
+do_async_request (SoupRequest *request)
+{
+ AsyncRequestData data;
+
+ data.body = g_string_new (NULL);
+ soup_request_send_async (request, NULL, async_request_sent, &data);
+
+ data.loop = g_main_loop_new (soup_session_get_async_context (soup_request_get_session (request)), TRUE);
+ g_main_loop_run (data.loop);
+ g_main_loop_unref (data.loop);
+
+ soup_assert_cmpmem (data.body->str, data.body->len,
+ index_buffer->data, index_buffer->length);
+ g_string_free (data.body, TRUE);
+}
+
+static void
+do_sync_request (SoupRequest *request)
+{
+ GInputStream *in;
+ GString *body;
+ char buffer[1024];
+ gssize nread;
+ GError *error = NULL;
+
+ in = soup_request_send (request, NULL, &error);
+ if (!in) {
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ return;
+ }
+
+ body = g_string_new (NULL);
+ do {
+ nread = g_input_stream_read (in, buffer, sizeof (buffer),
+ NULL, &error);
+ if (nread == -1) {
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ break;
+ }
+ g_string_append_len (body, buffer, nread);
+ } while (nread > 0);
+
+ g_input_stream_close (in, NULL, &error);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+ g_object_unref (in);
+
+ soup_assert_cmpmem (body->str, body->len, index_buffer->data, index_buffer->length);
+ g_string_free (body, TRUE);
+}
+
+static void
+do_request (const char *uri_string, gconstpointer type)
+{
+ SoupSession *session;
+ SoupRequest *request;
+ GError *error = NULL;
+
+ session = soup_test_session_new (GPOINTER_TO_SIZE (type),
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+
+ request = soup_session_request (session, uri_string, &error);
+ g_assert_no_error (error);
+
+ if (SOUP_IS_SESSION_ASYNC (session))
+ do_async_request (request);
+ else
+ do_sync_request (request);
+
+ g_object_unref (request);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_request_file_test (gconstpointer type)
+{
+ GFile *index;
+ char *uri_string;
+
+ index = g_file_new_for_path (g_test_get_filename (G_TEST_DIST, "index.txt", NULL));
+ uri_string = g_file_get_uri (index);
+ g_object_unref (index);
+
+ do_request (uri_string, type);
+ g_free (uri_string);
+}
+
+static void
+do_request_data_test (gconstpointer type)
+{
+ gchar *base64;
+ char *uri_string;
+
+ base64 = g_base64_encode ((const guchar *)index_buffer->data, index_buffer->length);
+ uri_string = g_strdup_printf ("data:text/plain;charset=utf8;base64,%s", base64);
+ g_free (base64);
+
+ do_request (uri_string, type);
+ g_free (uri_string);
+}
+
+static void
+do_request_gresource_test (gconstpointer type)
+{
+ do_request ("resource:///org/gnome/libsoup/tests/index.txt", type);
+}
+
+int
+main (int argc, char **argv)
+{
+ int ret;
+
+ test_init (argc, argv, NULL);
+
+ index_buffer = soup_test_get_index ();
+ soup_test_register_resources ();
+
+ g_test_add_data_func ("/resource/sync/file",
+ GSIZE_TO_POINTER (SOUP_TYPE_SESSION_SYNC),
+ do_request_file_test);
+ g_test_add_data_func ("/resource/sync/data",
+ GSIZE_TO_POINTER (SOUP_TYPE_SESSION_SYNC),
+ do_request_data_test);
+ g_test_add_data_func ("/resource/sync/gresource",
+ GSIZE_TO_POINTER (SOUP_TYPE_SESSION_SYNC),
+ do_request_gresource_test);
+
+ g_test_add_data_func ("/resource/async/file",
+ GSIZE_TO_POINTER (SOUP_TYPE_SESSION_ASYNC),
+ do_request_file_test);
+ g_test_add_data_func ("/resource/async/data",
+ GSIZE_TO_POINTER (SOUP_TYPE_SESSION_ASYNC),
+ do_request_data_test);
+ g_test_add_data_func ("/resource/async/gresource",
+ GSIZE_TO_POINTER (SOUP_TYPE_SESSION_ASYNC),
+ do_request_gresource_test);
+
+ ret = g_test_run ();
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/resources/feed.rdf b/tests/resources/feed.rdf
new file mode 100644
index 00000000..f3d9e276
--- /dev/null
+++ b/tests/resources/feed.rdf
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+
+<!-- RDF Site Summary (RSS) 1.0
+ http://groups.yahoo.com/group/rss-dev/files/specification.html
+ Section 5.3
+ -->
+
+<rdf:RDF
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://purl.org/rss/1.0/">
+
+ <channel rdf:about="http://www.xml.com/xml/news.rss">
+ <title>XML.com</title>
+ <link>http://xml.com/pub</link>
+ <description>
+ XML.com features a rich mix of information and services
+ for the XML community.
+ </description>
+
+ <image rdf:resource="http://xml.com/universal/images/xml_tiny.gif" />
+
+ <items>
+ <rdf:Seq>
+ <rdf:li resource="http://xml.com/pub/2000/08/09/xslt/xslt.html" />
+ <rdf:li resource="http://xml.com/pub/2000/08/09/rdfdb/index.html" />
+ </rdf:Seq>
+ </items>
+
+ <textinput rdf:resource="http://search.xml.com" />
+ </channel>
+
+</rdf:RDF>
diff --git a/tests/resources/home.jpg b/tests/resources/home.jpg
new file mode 100644
index 00000000..ac1f3bbc
--- /dev/null
+++ b/tests/resources/home.jpg
Binary files differ
diff --git a/tests/resources/home.png b/tests/resources/home.png
new file mode 100644
index 00000000..0bb82bac
--- /dev/null
+++ b/tests/resources/home.png
Binary files differ
diff --git a/tests/resources/html_binary.html b/tests/resources/html_binary.html
index 9200dd42..d443048c 100644
--- a/tests/resources/html_binary.html
+++ b/tests/resources/html_binary.html
@@ -1 +1 @@
-<HTML 
+<HTML
diff --git a/tests/resources/leading_space.html b/tests/resources/leading_space.html
new file mode 100644
index 00000000..a640d653
--- /dev/null
+++ b/tests/resources/leading_space.html
@@ -0,0 +1,12 @@
+
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title></title>
+</head>
+<body>
+<h1>GNOME!</h1>
+</body>
+</html>
diff --git a/tests/resources/test.aiff b/tests/resources/test.aiff
new file mode 100644
index 00000000..9a1ecbb2
--- /dev/null
+++ b/tests/resources/test.aiff
Binary files differ
diff --git a/tests/resources/test.mp4 b/tests/resources/test.mp4
new file mode 100644
index 00000000..d278c8ad
--- /dev/null
+++ b/tests/resources/test.mp4
Binary files differ
diff --git a/tests/resources/test.ogg b/tests/resources/test.ogg
new file mode 100644
index 00000000..e8f49ac3
--- /dev/null
+++ b/tests/resources/test.ogg
Binary files differ
diff --git a/tests/resources/test.wav b/tests/resources/test.wav
new file mode 100644
index 00000000..11660b29
--- /dev/null
+++ b/tests/resources/test.wav
Binary files differ
diff --git a/tests/resources/test.webm b/tests/resources/test.webm
new file mode 100644
index 00000000..7e53d0b4
--- /dev/null
+++ b/tests/resources/test.webm
Binary files differ
diff --git a/tests/resources/text.txt b/tests/resources/text.txt
new file mode 100644
index 00000000..ff7066f6
--- /dev/null
+++ b/tests/resources/text.txt
@@ -0,0 +1 @@
+This is just text.
diff --git a/tests/resources/tux.webp b/tests/resources/tux.webp
new file mode 100644
index 00000000..8764f066
--- /dev/null
+++ b/tests/resources/tux.webp
Binary files differ
diff --git a/tests/server-auth-test.c b/tests/server-auth-test.c
index 757e065a..f386f526 100644
--- a/tests/server-auth-test.c
+++ b/tests/server-auth-test.c
@@ -5,6 +5,8 @@
#include "test-utils.h"
+static SoupURI *base_uri;
+
static struct {
gboolean client_sent_basic, client_sent_digest;
gboolean server_requested_basic, server_requested_digest;
@@ -21,7 +23,7 @@ curl_exited (GPid pid, int status, gpointer data)
}
static void
-do_test (int n, SoupURI *base_uri, const char *path,
+do_test (SoupURI *base_uri, const char *path,
gboolean good_user, gboolean good_password,
gboolean offer_basic, gboolean offer_digest,
gboolean client_sends_basic, gboolean client_sends_digest,
@@ -34,18 +36,14 @@ do_test (int n, SoupURI *base_uri, const char *path,
GPid pid;
gboolean done;
- debug_printf (1, "%2d. %s, %soffer Basic, %soffer Digest, %s user, %s password\n",
- n, path, offer_basic ? "" : "don't ",
- offer_digest ? "" : "don't ",
- good_user ? "good" : "bad",
- good_password ? "good" : "bad");
-
uri = soup_uri_new_with_base (base_uri, path);
uri_str = soup_uri_to_string (uri, FALSE);
soup_uri_free (uri);
args = g_ptr_array_new ();
g_ptr_array_add (args, "curl");
+ g_ptr_array_add (args, "--noproxy");
+ g_ptr_array_add (args, "*");
g_ptr_array_add (args, "-f");
g_ptr_array_add (args, "-s");
if (offer_basic || offer_digest) {
@@ -86,135 +84,103 @@ do_test (int n, SoupURI *base_uri, const char *path,
g_ptr_array_free (args, TRUE);
g_free (uri_str);
- if (server_requests_basic != test_data.server_requested_basic) {
- errors++;
- if (test_data.server_requested_basic)
- debug_printf (1, " Server sent WWW-Authenticate: Basic, but shouldn't have!\n");
- else
- debug_printf (1, " Server didn't send WWW-Authenticate: Basic, but should have!\n");
- }
- if (server_requests_digest != test_data.server_requested_digest) {
- errors++;
- if (test_data.server_requested_digest)
- debug_printf (1, " Server sent WWW-Authenticate: Digest, but shouldn't have!\n");
- else
- debug_printf (1, " Server didn't send WWW-Authenticate: Digest, but should have!\n");
- }
- if (client_sends_basic != test_data.client_sent_basic) {
- errors++;
- if (test_data.client_sent_basic)
- debug_printf (1, " Client sent Authorization: Basic, but shouldn't have!\n");
- else
- debug_printf (1, " Client didn't send Authorization: Basic, but should have!\n");
- }
- if (client_sends_digest != test_data.client_sent_digest) {
- errors++;
- if (test_data.client_sent_digest)
- debug_printf (1, " Client sent Authorization: Digest, but shouldn't have!\n");
- else
- debug_printf (1, " Client didn't send Authorization: Digest, but should have!\n");
- }
- if (success && !test_data.succeeded) {
- errors++;
- debug_printf (1, " Should have succeeded, but didn't!\n");
- } else if (!success && test_data.succeeded) {
- errors++;
- debug_printf (1, " Should not have succeeded, but did!\n");
- }
+ g_assert_cmpint (server_requests_basic, ==, test_data.server_requested_basic);
+ g_assert_cmpint (server_requests_digest, ==, test_data.server_requested_digest);
+ g_assert_cmpint (client_sends_basic, ==, test_data.client_sent_basic);
+ g_assert_cmpint (client_sends_digest, ==, test_data.client_sent_digest);
+
+ g_assert_cmpint (success, ==, test_data.succeeded);
}
+#define TEST_USES_BASIC(t) (((t) & 1) == 1)
+#define TEST_USES_DIGEST(t) (((t) & 2) == 2)
+#define TEST_GOOD_USER(t) (((t) & 4) == 4)
+#define TEST_GOOD_PASSWORD(t) (((t) & 8) == 8)
+
+#define TEST_GOOD_AUTH(t) (TEST_GOOD_USER (t) && TEST_GOOD_PASSWORD (t))
+#define TEST_PREEMPTIVE_BASIC(t) (TEST_USES_BASIC (t) && !TEST_USES_DIGEST (t))
+
static void
-do_auth_tests (SoupURI *base_uri)
+do_server_auth_test (gconstpointer data)
{
- int i, n = 1;
- gboolean use_basic, use_digest, good_user, good_password;
- gboolean preemptive_basic, good_auth;
-
- for (i = 0; i < 16; i++) {
- use_basic = (i & 1) == 1;
- use_digest = (i & 2) == 2;
- good_user = (i & 4) == 4;
- good_password = (i & 8) == 8;
-
- good_auth = good_user && good_password;
-
- /* Curl will preemptively send Basic if it's told to
- * use Basic but not Digest.
- */
- preemptive_basic = use_basic && !use_digest;
-
- /* 1. No auth required. The server will ignore the
- * Authorization headers completely, and the request
- * will always succeed.
- */
- do_test (n++, base_uri, "/foo",
- good_user, good_password,
- /* request */
- use_basic, use_digest,
- /* expected from client */
- preemptive_basic, FALSE,
- /* expected from server */
- FALSE, FALSE,
- /* success? */
- TRUE);
-
- /* 2. Basic auth required. The server will send
- * "WWW-Authenticate: Basic" if the client fails to
- * send an Authorization: Basic on the first request,
- * or if it sends a bad password.
- */
- do_test (n++, base_uri, "/Basic/foo",
- good_user, good_password,
- /* request */
- use_basic, use_digest,
- /* expected from client */
- use_basic, FALSE,
- /* expected from server */
- !preemptive_basic || !good_auth, FALSE,
- /* success? */
- use_basic && good_auth);
-
- /* 3. Digest auth required. Simpler than the basic
- * case because the client can't send Digest auth
- * premptively.
- */
- do_test (n++, base_uri, "/Digest/foo",
- good_user, good_password,
- /* request */
- use_basic, use_digest,
- /* expected from client */
- preemptive_basic, use_digest,
- /* expected from server */
- FALSE, TRUE,
- /* success? */
- use_digest && good_auth);
-
- /* 4. Any auth required. */
- do_test (n++, base_uri, "/Any/foo",
- good_user, good_password,
- /* request */
- use_basic, use_digest,
- /* expected from client */
- preemptive_basic, use_digest,
- /* expected from server */
- !preemptive_basic || !good_auth, !preemptive_basic || !good_auth,
- /* success? */
- (use_basic || use_digest) && good_auth);
-
- /* 5. No auth required again. (Makes sure that
- * SOUP_AUTH_DOMAIN_REMOVE_PATH works.)
- */
- do_test (n++, base_uri, "/Any/Not/foo",
- good_user, good_password,
- /* request */
- use_basic, use_digest,
- /* expected from client */
- preemptive_basic, FALSE,
- /* expected from server */
- FALSE, FALSE,
- /* success? */
- TRUE);
- }
+ int i = GPOINTER_TO_INT (data);
+
+#ifndef HAVE_CURL
+ g_test_skip ("/usr/bin/curl is not available");
+ return;
+#endif
+
+ /* 1. No auth required. The server will ignore the
+ * Authorization headers completely, and the request
+ * will always succeed.
+ */
+ do_test (base_uri, "/foo",
+ TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
+ /* request */
+ TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
+ /* expected from client */
+ TEST_PREEMPTIVE_BASIC (i), FALSE,
+ /* expected from server */
+ FALSE, FALSE,
+ /* success? */
+ TRUE);
+
+ /* 2. Basic auth required. The server will send
+ * "WWW-Authenticate: Basic" if the client fails to
+ * send an Authorization: Basic on the first request,
+ * or if it sends a bad password.
+ */
+ do_test (base_uri, "/Basic/foo",
+ TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
+ /* request */
+ TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
+ /* expected from client */
+ TEST_USES_BASIC (i), FALSE,
+ /* expected from server */
+ !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), FALSE,
+ /* success? */
+ TEST_USES_BASIC (i) && TEST_GOOD_AUTH (i));
+
+ /* 3. Digest auth required. Simpler than the basic
+ * case because the client can't send Digest auth
+ * premptively.
+ */
+ do_test (base_uri, "/Digest/foo",
+ 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));
+
+ /* 4. Any auth required. */
+ do_test (base_uri, "/Any/foo",
+ 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 */
+ !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i),
+ /* success? */
+ (TEST_USES_BASIC (i) || TEST_USES_DIGEST (i)) && TEST_GOOD_AUTH (i));
+
+ /* 5. No auth required again. (Makes sure that
+ * SOUP_AUTH_DOMAIN_REMOVE_PATH works.)
+ */
+ do_test (base_uri, "/Any/Not/foo",
+ TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
+ /* request */
+ TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
+ /* expected from client */
+ TEST_PREEMPTIVE_BASIC (i), FALSE,
+ /* expected from server */
+ FALSE, FALSE,
+ /* success? */
+ TRUE);
}
static gboolean
@@ -311,8 +277,8 @@ main (int argc, char **argv)
{
GMainLoop *loop;
SoupServer *server;
- SoupURI *uri;
SoupAuthDomain *auth_domain;
+ int ret;
test_init (argc, argv, no_test_entry);
@@ -345,13 +311,45 @@ main (int argc, char **argv)
loop = g_main_loop_new (NULL, TRUE);
if (run_tests) {
- uri = soup_uri_new ("http://127.0.0.1");
- soup_uri_set_port (uri, soup_server_get_port (server));
- do_auth_tests (uri);
- soup_uri_free (uri);
+ 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;
+
+ if (!TEST_GOOD_USER (i) && !TEST_GOOD_PASSWORD (i))
+ continue;
+ if (TEST_USES_BASIC (i)) {
+ if (TEST_USES_DIGEST (i))
+ authtypes = "basic+digest";
+ else
+ authtypes = "basic";
+ } else {
+ if (TEST_USES_DIGEST (i))
+ authtypes = "digest";
+ else
+ authtypes = "none";
+ }
+
+ path = g_strdup_printf ("/server-auth/%s/%s-user%c%s-password",
+ authtypes,
+ TEST_GOOD_USER (i) ? "good" : "bad",
+ TEST_GOOD_USER (i) ? '/' : '\0',
+ TEST_GOOD_PASSWORD (i) ? "good" : "bad");
+ g_test_add_data_func (path, GINT_TO_POINTER (i), do_server_auth_test);
+ g_free (path);
+ }
+
+ ret = g_test_run ();
+
+ soup_uri_free (base_uri);
} else {
g_print ("Listening on port %d\n", soup_server_get_port (server));
g_main_loop_run (loop);
+ ret = 0;
}
g_main_loop_unref (loop);
@@ -359,5 +357,5 @@ main (int argc, char **argv)
if (run_tests)
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/server-test.c b/tests/server-test.c
new file mode 100644
index 00000000..0c980908
--- /dev/null
+++ b/tests/server-test.c
@@ -0,0 +1,329 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2007-2012 Red Hat, Inc.
+ */
+
+#include "test-utils.h"
+
+SoupServer *server, *ssl_server;
+SoupURI *base_uri, *ssl_base_uri;
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ soup_message_headers_append (msg->response_headers,
+ "X-Handled-By", "server_callback");
+
+ if (!strcmp (path, "*")) {
+ soup_test_assert (FALSE, "default server_callback got request for '*'");
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC, "index", 5);
+}
+
+static void
+server_star_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ soup_message_headers_append (msg->response_headers,
+ "X-Handled-By", "star_callback");
+
+ if (strcmp (path, "*") != 0) {
+ soup_test_assert (FALSE, "server_star_callback got request for '%s'", path);
+ soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (msg->method != SOUP_METHOD_OPTIONS) {
+ soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+/* Server handlers for "*" work but are separate from handlers for
+ * all other URIs. #590751
+ */
+static void
+do_star_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *star_uri;
+ const char *handled_by;
+
+ g_test_bug ("590751");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ star_uri = soup_uri_copy (base_uri);
+ soup_uri_set_path (star_uri, "*");
+
+ debug_printf (1, " Testing with no handler\n");
+ msg = soup_message_new_from_uri ("OPTIONS", star_uri);
+ soup_session_send_message (session, msg);
+
+ soup_test_assert_message_status (msg, SOUP_STATUS_NOT_FOUND);
+ handled_by = soup_message_headers_get_one (msg->response_headers,
+ "X-Handled-By");
+ g_assert_cmpstr (handled_by, ==, NULL);
+ g_object_unref (msg);
+
+ soup_server_add_handler (server, "*", server_star_callback, NULL, NULL);
+
+ debug_printf (1, " Testing with handler\n");
+ msg = soup_message_new_from_uri ("OPTIONS", star_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, ==, "star_callback");
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+ soup_uri_free (star_uri);
+}
+
+static void
+do_one_server_aliases_test (SoupURI *uri,
+ const char *alias,
+ gboolean succeed)
+{
+ GSocketClient *client;
+ GSocketConnectable *addr;
+ GSocketConnection *conn;
+ GInputStream *in;
+ GOutputStream *out;
+ GError *error = NULL;
+ GString *req;
+ static char buf[1024];
+
+ debug_printf (1, " %s via %s\n", alias, uri->scheme);
+
+ /* There's no way to make libsoup's client side send an absolute
+ * URI (to a non-proxy server), so we have to fake this.
+ */
+
+ client = g_socket_client_new ();
+ if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
+ g_socket_client_set_tls (client, TRUE);
+ g_socket_client_set_tls_validation_flags (client, 0);
+ }
+ addr = g_network_address_new (uri->host, uri->port);
+
+ conn = g_socket_client_connect (client, addr, NULL, &error);
+ g_object_unref (addr);
+ g_object_unref (client);
+ if (!conn) {
+ g_assert_no_error (error);
+ g_error_free (error);
+ return;
+ }
+
+ in = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+ out = g_io_stream_get_output_stream (G_IO_STREAM (conn));
+
+ req = g_string_new (NULL);
+ g_string_append_printf (req, "GET %s://%s:%d HTTP/1.1\r\n",
+ alias, uri->host, uri->port);
+ g_string_append_printf (req, "Host: %s:%d\r\n",
+ uri->host, uri->port);
+ g_string_append (req, "Connection: close\r\n\r\n");
+
+ if (!g_output_stream_write_all (out, req->str, req->len, NULL, NULL, &error)) {
+ g_assert_no_error (error);
+ g_error_free (error);
+ g_object_unref (conn);
+ g_string_free (req, TRUE);
+ return;
+ }
+ g_string_free (req, TRUE);
+
+ if (!g_input_stream_read_all (in, buf, sizeof (buf), NULL, NULL, &error)) {
+ g_assert_no_error (error);
+ g_error_free (error);
+ g_object_unref (conn);
+ return;
+ }
+
+ if (succeed)
+ g_assert_true (g_str_has_prefix (buf, "HTTP/1.1 200 "));
+ else
+ g_assert_true (g_str_has_prefix (buf, "HTTP/1.1 400 "));
+
+ g_io_stream_close (G_IO_STREAM (conn), NULL, NULL);
+ g_object_unref (conn);
+}
+
+static void
+do_server_aliases_test (void)
+{
+ char *http_good[] = { "http", "dav", NULL };
+ char *http_bad[] = { "https", "davs", "fred", NULL };
+ char *https_good[] = { "https", "davs", NULL };
+ char *https_bad[] = { "http", "dav", "fred", NULL };
+ int i;
+
+ g_test_bug ("703694");
+
+ for (i = 0; http_good[i]; i++)
+ do_one_server_aliases_test (base_uri, http_good[i], TRUE);
+ for (i = 0; http_bad[i]; i++)
+ do_one_server_aliases_test (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);
+ for (i = 0; https_bad[i]; i++)
+ do_one_server_aliases_test (ssl_base_uri, https_bad[i], FALSE);
+ }
+}
+
+static void
+do_dot_dot_test (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *uri;
+
+ g_test_bug ("667635");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+
+ uri = soup_uri_new_with_base (base_uri, "/..%2ftest");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ soup_session_send_message (session, msg);
+ soup_test_assert_message_status (msg, SOUP_STATUS_BAD_REQUEST);
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+}
+
+static void
+ipv6_server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ const char *host;
+ char expected_host[128];
+
+ g_snprintf (expected_host, sizeof (expected_host),
+ "[::1]:%d", soup_server_get_port (server));
+
+ host = soup_message_headers_get_one (msg->request_headers, "Host");
+ g_assert_cmpstr (host, ==, expected_host);
+
+ if (g_test_failed ())
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ else
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static void
+do_ipv6_test (void)
+{
+ SoupServer *ipv6_server;
+ SoupURI *ipv6_uri;
+ SoupAddress *ipv6_addr;
+ SoupSession *session;
+ SoupMessage *msg;
+
+ 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");
+ 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));
+
+ 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);
+ 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);
+ 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)
+{
+ char *http_aliases[] = { "dav", NULL };
+ char *https_aliases[] = { "davs", NULL };
+ int ret;
+
+ test_init (argc, argv, NULL);
+
+ 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));
+
+ g_object_set (G_OBJECT (server),
+ SOUP_SERVER_HTTP_ALIASES, http_aliases,
+ NULL);
+
+ 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_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);
+
+ ret = g_test_run ();
+
+ soup_uri_free (base_uri);
+ soup_test_server_quit_unref (server);
+
+ if (tls_available) {
+ soup_uri_free (ssl_base_uri);
+ soup_test_server_quit_unref (ssl_server);
+ }
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/session-test.c b/tests/session-test.c
new file mode 100644
index 00000000..15072058
--- /dev/null
+++ b/tests/session-test.c
@@ -0,0 +1,396 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+#include "test-utils.h"
+
+static gboolean server_processed_message;
+static gboolean timeout;
+static GMainLoop *loop;
+static SoupMessagePriority expected_priorities[3];
+
+static gboolean
+timeout_cb (gpointer user_data)
+{
+ gboolean *timeout = user_data;
+
+ *timeout = TRUE;
+ return FALSE;
+}
+
+static void
+server_handler (SoupServer *server,
+ SoupMessage *msg,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
+{
+ if (!strcmp (path, "/request-timeout")) {
+ GMainContext *context = soup_server_get_async_context (server);
+ GSource *timer;
+
+ timer = g_timeout_source_new (100);
+ g_source_set_callback (timer, timeout_cb, &timeout, NULL);
+ g_source_attach (timer, context);
+ g_source_unref (timer);
+ } else
+ server_processed_message = TRUE;
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+ soup_message_set_response (msg, "text/plain",
+ SOUP_MEMORY_STATIC,
+ "ok\r\n", 4);
+}
+
+static void
+finished_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ gboolean *finished = user_data;
+
+ *finished = TRUE;
+}
+
+static void
+cancel_message_cb (SoupMessage *msg, gpointer session)
+{
+ soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
+ g_main_loop_quit (loop);
+}
+
+static void
+do_test_for_session (SoupSession *session,
+ const char *uri,
+ gboolean queue_is_async,
+ gboolean send_is_blocking,
+ gboolean cancel_is_immediate)
+{
+ SoupMessage *msg;
+ gboolean finished, local_timeout;
+ guint timeout_id;
+ char *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);
+ soup_session_send_message (session, msg);
+ g_object_unref (msg);
+
+ msg = soup_message_new ("GET", uri);
+ server_processed_message = timeout = finished = FALSE;
+ soup_session_queue_message (session, msg, finished_cb, &finished);
+ while (!timeout)
+ g_usleep (100);
+ debug_printf (2, " got timeout\n");
+
+ if (queue_is_async) {
+ g_assert_false (server_processed_message);
+ debug_printf (2, " waiting for finished\n");
+ while (!finished)
+ g_main_context_iteration (NULL, TRUE);
+ g_assert_true (server_processed_message);
+ } else {
+ g_assert_true (server_processed_message);
+ g_assert_false (finished);
+ debug_printf (2, " waiting for finished\n");
+ while (!finished)
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ debug_printf (1, " send_message\n");
+ msg = soup_message_new ("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_assert_true (server_processed_message);
+
+ if (send_is_blocking) {
+ soup_test_assert (!local_timeout,
+ "send_message ran main loop");
+ } else {
+ soup_test_assert (local_timeout,
+ "send_message didn't run main loop");
+ }
+
+ if (!local_timeout)
+ g_source_remove (timeout_id);
+
+ if (!queue_is_async)
+ return;
+
+ debug_printf (1, " cancel_message\n");
+ msg = soup_message_new ("GET", uri);
+ g_object_ref (msg);
+ finished = FALSE;
+ soup_session_queue_message (session, msg, finished_cb, &finished);
+ g_signal_connect (msg, "wrote-headers",
+ G_CALLBACK (cancel_message_cb), session);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ if (cancel_is_immediate)
+ g_assert_true (finished);
+ else
+ g_assert_false (finished);
+
+ if (!finished) {
+ debug_printf (2, " waiting for finished\n");
+ while (!finished)
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED);
+ g_object_unref (msg);
+}
+
+static void
+do_plain_tests (gconstpointer uri)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+ do_test_for_session (session, uri, TRUE, TRUE, FALSE);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_async_tests (gconstpointer uri)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ do_test_for_session (session, uri, TRUE, FALSE, TRUE);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_sync_tests (gconstpointer uri)
+{
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+ do_test_for_session (session, uri, FALSE, TRUE, FALSE);
+ soup_test_session_abort_unref (session);
+}
+
+static void
+priority_test_finished_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ guint *finished_count = user_data;
+ SoupMessagePriority priority = soup_message_get_priority (msg);
+
+ debug_printf (1, " received message %d with priority %d\n",
+ *finished_count, priority);
+
+ soup_test_assert (priority == expected_priorities[*finished_count],
+ "message %d should have priority %d (%d found)",
+ *finished_count, expected_priorities[*finished_count], priority);
+
+ (*finished_count)++;
+}
+
+static void
+do_priority_tests (gconstpointer data)
+{
+ const char *uri = data;
+ SoupSession *session;
+ int i, finished_count = 0;
+ SoupMessagePriority priorities[] =
+ { SOUP_MESSAGE_PRIORITY_LOW,
+ SOUP_MESSAGE_PRIORITY_HIGH,
+ SOUP_MESSAGE_PRIORITY_NORMAL };
+
+ g_test_bug ("696277");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ g_object_set (session, "max-conns", 1, NULL);
+
+ expected_priorities[0] = SOUP_MESSAGE_PRIORITY_HIGH;
+ expected_priorities[1] = SOUP_MESSAGE_PRIORITY_NORMAL;
+ expected_priorities[2] = SOUP_MESSAGE_PRIORITY_LOW;
+
+ for (i = 0; i < 3; i++) {
+ char *msg_uri;
+ SoupMessage *msg;
+
+ msg_uri = g_strdup_printf ("%s/%d", uri, i);
+ msg = soup_message_new ("GET", uri);
+ g_free (msg_uri);
+
+ soup_message_set_priority (msg, priorities[i]);
+ soup_session_queue_message (session, msg, priority_test_finished_cb, &finished_count);
+ }
+
+ debug_printf (2, " waiting for finished\n");
+ while (finished_count != 3)
+ g_main_context_iteration (NULL, TRUE);
+
+ soup_test_session_abort_unref (session);
+}
+
+static void
+test_session_properties (const char *name,
+ SoupSession *session,
+ GProxyResolver *expected_proxy_resolver,
+ GTlsDatabase *expected_tls_database)
+{
+ GProxyResolver *proxy_resolver = NULL;
+ GTlsDatabase *tlsdb = NULL;
+
+ g_object_get (G_OBJECT (session),
+ SOUP_SESSION_PROXY_RESOLVER, &proxy_resolver,
+ SOUP_SESSION_TLS_DATABASE, &tlsdb,
+ NULL);
+
+ soup_test_assert (proxy_resolver == expected_proxy_resolver,
+ "%s has %s proxy resolver",
+ name, proxy_resolver ? (expected_proxy_resolver ? "wrong" : "a") : "no");
+ soup_test_assert (tlsdb == expected_tls_database,
+ "%s has %s TLS database",
+ name, tlsdb ? (expected_tls_database ? "wrong" : "a") : "no");
+
+ g_clear_object (&proxy_resolver);
+ g_clear_object (&tlsdb);
+}
+
+static void
+do_property_tests (void)
+{
+ SoupSession *session;
+ GProxyResolver *proxy_resolver, *default_proxy_resolver;
+ GTlsDatabase *tlsdb, *default_tlsdb;
+ SoupURI *uri;
+
+ g_test_bug ("708696");
+
+ default_proxy_resolver = g_proxy_resolver_get_default ();
+ default_tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+
+ /* NOTE: We intentionally do not use soup_test_session_new() here */
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ NULL);
+ test_session_properties ("Base plain session", session,
+ default_proxy_resolver, default_tlsdb);
+ g_object_unref (session);
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_PROXY_RESOLVER, NULL,
+ NULL);
+ test_session_properties ("Session with NULL :proxy-resolver", session,
+ NULL, default_tlsdb);
+ g_object_unref (session);
+
+ proxy_resolver = g_simple_proxy_resolver_new (NULL, NULL);
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_PROXY_RESOLVER, proxy_resolver,
+ NULL);
+ test_session_properties ("Session with non-NULL :proxy-resolver", session,
+ proxy_resolver, default_tlsdb);
+ g_object_unref (proxy_resolver);
+ g_object_unref (session);
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_PROXY_URI, NULL,
+ NULL);
+ test_session_properties ("Session with NULL :proxy-uri", session,
+ NULL, default_tlsdb);
+ g_object_unref (session);
+
+ uri = soup_uri_new ("http://example.com/");
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_PROXY_URI, uri,
+ NULL);
+ g_object_get (G_OBJECT (session),
+ SOUP_SESSION_PROXY_RESOLVER, &proxy_resolver,
+ NULL);
+ test_session_properties ("Session with non-NULL :proxy-uri", session,
+ proxy_resolver, default_tlsdb);
+ g_assert_cmpstr (G_OBJECT_TYPE_NAME (proxy_resolver), ==, "GSimpleProxyResolver");
+ g_object_unref (proxy_resolver);
+ g_object_unref (session);
+ soup_uri_free (uri);
+
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_REMOVE_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_URI_RESOLVER,
+ NULL);
+ test_session_properties ("Session with removed proxy resolver feature", session,
+ NULL, default_tlsdb);
+ g_object_unref (session);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_TLS_DATABASE, NULL,
+ NULL);
+ test_session_properties ("Session with NULL :tls-database", session,
+ default_proxy_resolver, NULL);
+ g_object_unref (session);
+
+ /* g_tls_file_database_new() will fail with the dummy backend,
+ * so we can only do this test if we have a real TLS backend.
+ */
+ if (tls_available) {
+ GError *error = NULL;
+
+ tlsdb = g_tls_file_database_new (g_test_get_filename (G_TEST_DIST,
+ "test-cert.pem",
+ NULL), &error);
+ g_assert_no_error (error);
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_TLS_DATABASE, tlsdb,
+ NULL);
+ test_session_properties ("Session with non-NULL :tls-database", session,
+ default_proxy_resolver, tlsdb);
+ g_object_unref (tlsdb);
+ g_object_unref (session);
+ }
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, FALSE,
+ NULL);
+ test_session_properties ("Session with :ssl-use-system-ca-file FALSE", session,
+ default_proxy_resolver, NULL);
+ g_object_unref (session);
+
+ session = g_object_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+ NULL);
+ test_session_properties ("Session with :ssl-use-system-ca-file TRUE", session,
+ default_proxy_resolver, default_tlsdb);
+ g_object_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ char *uri, *timeout_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);
+
+ g_test_add_data_func ("/session/SoupSession", uri, do_plain_tests);
+ g_test_add_data_func ("/session/SoupSessionAsync", uri, do_async_tests);
+ g_test_add_data_func ("/session/SoupSessionSync", uri, do_sync_tests);
+ g_test_add_data_func ("/session/priority", uri, do_priority_tests);
+ g_test_add_func ("/session/property", do_property_tests);
+
+ ret = g_test_run ();
+
+ g_free (uri);
+ g_free (timeout_uri);
+ soup_test_server_quit_unref (server);
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c
index dd4eb4aa..32fad9fc 100644
--- a/tests/sniffing-test.c
+++ b/tests/sniffing-test.c
@@ -16,8 +16,8 @@ server_callback (SoupServer *server, SoupMessage *msg,
{
GError *error = NULL;
char *query_key;
- char *contents;
- gsize length = 0, offset;
+ SoupBuffer *response = NULL;
+ gsize offset;
gboolean empty_response = FALSE;
if (msg->method != SOUP_METHOD_GET) {
@@ -40,41 +40,35 @@ server_callback (SoupServer *server, SoupMessage *msg,
}
if (!strcmp (path, "/mbox")) {
- if (empty_response) {
- contents = g_strdup ("");
- length = 0;
- } else {
- g_file_get_contents (SRCDIR "/resources/mbox",
- &contents, &length,
- &error);
- }
-
- if (error) {
- g_error ("%s", error->message);
- g_error_free (error);
- exit (1);
+ if (!empty_response) {
+ response = soup_test_load_resource ("mbox", &error);
+ g_assert_no_error (error);
}
soup_message_headers_append (msg->response_headers,
"Content-Type", "text/plain");
}
- if (g_str_has_prefix (path, "/text_or_binary/")) {
+ if (g_str_has_prefix (path, "/nosniff/")) {
char *base_name = g_path_get_basename (path);
- char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
-
- g_file_get_contents (file_name,
- &contents, &length,
- &error);
+ response = soup_test_load_resource (base_name, &error);
+ g_assert_no_error (error);
g_free (base_name);
- g_free (file_name);
- if (error) {
- g_error ("%s", error->message);
- g_error_free (error);
- exit (1);
- }
+ soup_message_headers_append (msg->response_headers,
+ "X-Content-Type-Options", "nosniff");
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "no/sniffing-allowed");
+ }
+
+ if (g_str_has_prefix (path, "/text_or_binary/") || g_str_has_prefix (path, "/apache_bug/")) {
+ char *base_name = g_path_get_basename (path);
+
+ response = soup_test_load_resource (base_name, &error);
+ g_assert_no_error (error);
+ g_free (base_name);
soup_message_headers_append (msg->response_headers,
"Content-Type", "text/plain");
@@ -82,20 +76,10 @@ server_callback (SoupServer *server, SoupMessage *msg,
if (g_str_has_prefix (path, "/unknown/")) {
char *base_name = g_path_get_basename (path);
- char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
-
- g_file_get_contents (file_name,
- &contents, &length,
- &error);
+ response = soup_test_load_resource (base_name, &error);
+ g_assert_no_error (error);
g_free (base_name);
- g_free (file_name);
-
- if (error) {
- g_error ("%s", error->message);
- g_error_free (error);
- exit (1);
- }
soup_message_headers_append (msg->response_headers,
"Content-Type", "UNKNOWN/unknown");
@@ -106,20 +90,10 @@ server_callback (SoupServer *server, SoupMessage *msg,
char *ptr;
char *base_name = g_path_get_basename (path);
- char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
-
- g_file_get_contents (file_name,
- &contents, &length,
- &error);
+ response = soup_test_load_resource (base_name, &error);
+ g_assert_no_error (error);
g_free (base_name);
- g_free (file_name);
-
- if (error) {
- g_error ("%s", error->message);
- g_error_free (error);
- exit (1);
- }
/* Hack to allow passing type in the URI */
ptr = g_strrstr (components[2], "_");
@@ -132,20 +106,10 @@ server_callback (SoupServer *server, SoupMessage *msg,
if (g_str_has_prefix (path, "/multiple_headers/")) {
char *base_name = g_path_get_basename (path);
- char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
-
- g_file_get_contents (file_name,
- &contents, &length,
- &error);
+ response = soup_test_load_resource (base_name, &error);
+ g_assert_no_error (error);
g_free (base_name);
- g_free (file_name);
-
- if (error) {
- g_error ("%s", error->message);
- g_error_free (error);
- exit (1);
- }
soup_message_headers_append (msg->response_headers,
"Content-Type", "text/xml");
@@ -153,15 +117,18 @@ server_callback (SoupServer *server, SoupMessage *msg,
"Content-Type", "text/plain");
}
- for (offset = 0; offset < length; offset += 500) {
- soup_message_body_append (msg->response_body,
- SOUP_MEMORY_COPY,
- contents + offset,
- MIN(500, length - offset));
+ if (response) {
+ for (offset = 0; offset < response->length; offset += 500) {
+ soup_message_body_append (msg->response_body,
+ SOUP_MEMORY_COPY,
+ response->data + offset,
+ MIN (500, response->length - offset));
+ }
+
+ soup_buffer_free (response);
}
- soup_message_body_complete (msg->response_body);
- g_free (contents);
+ soup_message_body_complete (msg->response_body);
}
static gboolean
@@ -181,10 +148,8 @@ content_sniffed (SoupMessage *msg, char *content_type, GHashTable *params, gpoin
debug_printf (2, " content-sniffed -> %s\n", content_type);
- if (g_object_get_data (G_OBJECT (msg), "got-chunk")) {
- debug_printf (1, " got-chunk got emitted before content-sniffed\n");
- errors++;
- }
+ soup_test_assert (g_object_get_data (G_OBJECT (msg), "got-chunk") == NULL,
+ "got-chunk got emitted before content-sniffed");
g_object_set_data (G_OBJECT (msg), "content-sniffed", GINT_TO_POINTER (TRUE));
@@ -202,10 +167,8 @@ got_headers (SoupMessage *msg, gpointer data)
debug_printf (2, " got-headers\n");
- if (g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
- debug_printf (1, " content-sniffed got emitted before got-headers\n");
- errors++;
- }
+ soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") == NULL,
+ "content-sniffed got emitted before got-headers");
g_object_set_data (G_OBJECT (msg), "got-headers", GINT_TO_POINTER (TRUE));
@@ -233,13 +196,6 @@ got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer data)
}
static void
-finished (SoupSession *session, SoupMessage *msg, gpointer data)
-{
- GMainLoop *loop = (GMainLoop*)data;
- g_main_loop_quit (loop);
-}
-
-static void
do_signals_test (gboolean should_content_sniff,
gboolean should_pause,
gboolean should_accumulate,
@@ -248,9 +204,7 @@ do_signals_test (gboolean should_content_sniff,
{
SoupURI *uri = soup_uri_new_with_base (base_uri, "/mbox");
SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
- GMainLoop *loop = g_main_loop_new (NULL, TRUE);
- char *contents;
- gsize length;
+ SoupBuffer *expected;
GError *error = NULL;
SoupBuffer *body = NULL;
@@ -283,34 +237,21 @@ do_signals_test (gboolean should_content_sniff,
"signal::content_sniffed", content_sniffed, GINT_TO_POINTER (should_pause),
NULL);
- g_object_ref (msg);
- soup_session_queue_message (session, msg, finished, loop);
-
- g_main_loop_run (loop);
-
- if (!should_content_sniff &&
- g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
- debug_printf (1, " content-sniffed got emitted without a sniffer\n");
- errors++;
- } else if (should_content_sniff &&
- !g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
- debug_printf (1, " content-sniffed did not get emitted\n");
- errors++;
- }
+ soup_session_send_message (session, msg);
- if (empty_response) {
- contents = g_strdup ("");
- length = 0;
+ if (should_content_sniff) {
+ soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") != NULL,
+ "content-sniffed did not get emitted");
} else {
- g_file_get_contents (SRCDIR "/resources/mbox",
- &contents, &length,
- &error);
+ soup_test_assert (g_object_get_data (G_OBJECT (msg), "content-sniffed") == NULL,
+ "content-sniffed got emitted without a sniffer");
}
- if (error) {
- g_error ("%s", error->message);
- g_error_free (error);
- exit (1);
+ if (empty_response)
+ expected = soup_buffer_new (SOUP_MEMORY_STATIC, "", 0);
+ else {
+ expected = soup_test_load_resource ("mbox", &error);
+ g_assert_no_error (error);
}
if (!should_accumulate && chunk_data)
@@ -318,17 +259,12 @@ do_signals_test (gboolean should_content_sniff,
else if (msg->response_body)
body = soup_message_body_flatten (msg->response_body);
- if (body && body->length != length) {
- debug_printf (1, " lengths do not match\n");
- errors++;
+ if (body) {
+ soup_assert_cmpmem (body->data, body->length,
+ expected->data, expected->length);
}
- if (body && memcmp (body->data, contents, length)) {
- debug_printf (1, " downloaded data does not match\n");
- errors++;
- }
-
- g_free (contents);
+ soup_buffer_free (expected);
if (body)
soup_buffer_free (body);
if (chunk_data) {
@@ -338,7 +274,42 @@ do_signals_test (gboolean should_content_sniff,
soup_uri_free (uri);
g_object_unref (msg);
- g_main_loop_unref (loop);
+}
+
+static void
+do_signals_tests (gconstpointer data)
+{
+ gboolean should_content_sniff = GPOINTER_TO_INT (data);
+
+ if (!should_content_sniff)
+ soup_session_remove_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
+
+ do_signals_test (should_content_sniff,
+ FALSE, FALSE, FALSE, FALSE);
+ do_signals_test (should_content_sniff,
+ FALSE, FALSE, TRUE, FALSE);
+ do_signals_test (should_content_sniff,
+ FALSE, TRUE, FALSE, FALSE);
+ do_signals_test (should_content_sniff,
+ FALSE, TRUE, TRUE, FALSE);
+
+ do_signals_test (should_content_sniff,
+ TRUE, TRUE, FALSE, FALSE);
+ do_signals_test (should_content_sniff,
+ TRUE, TRUE, TRUE, FALSE);
+ do_signals_test (should_content_sniff,
+ TRUE, FALSE, FALSE, FALSE);
+ do_signals_test (should_content_sniff,
+ TRUE, FALSE, TRUE, FALSE);
+
+ /* FIXME g_test_bug ("587907") */
+ do_signals_test (should_content_sniff,
+ TRUE, TRUE, FALSE, TRUE);
+ do_signals_test (should_content_sniff,
+ TRUE, TRUE, TRUE, TRUE);
+
+ if (!should_content_sniff)
+ soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
}
static void
@@ -347,102 +318,125 @@ sniffing_content_sniffed (SoupMessage *msg, const char *content_type,
{
char **sniffed_type = (char **)data;
GString *full_header;
- GList *keys;
- GList *iter;
-
- if (params == NULL) {
- *sniffed_type = g_strdup (content_type);
- return;
- }
+ GHashTableIter iter;
+ gpointer key, value;
full_header = g_string_new (content_type);
- g_string_append (full_header, "; ");
-
- keys = g_hash_table_get_keys (params);
- for (iter = keys; iter != NULL; iter = iter->next) {
- const gchar *value = (const gchar*) g_hash_table_lookup (params, iter->data);
+ g_hash_table_iter_init (&iter, params);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (full_header->len)
+ g_string_append (full_header, "; ");
soup_header_g_string_append_param (full_header,
- (const gchar*) iter->data,
- value);
+ (const char *) key,
+ (const char *) value);
}
- *sniffed_type = full_header->str;
-
- g_string_free (full_header, FALSE);
- g_list_free (keys);
+ *sniffed_type = g_string_free (full_header, FALSE);
}
static void
test_sniffing (const char *path, const char *expected_type)
{
- SoupURI *uri = soup_uri_new_with_base (base_uri, path);
- SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
- GMainLoop *loop = g_main_loop_new (NULL, TRUE);
+ SoupURI *uri;
+ SoupMessage *msg;
+ SoupRequest *req;
+ GInputStream *stream;
char *sniffed_type = NULL;
+ const char *req_sniffed_type;
+ GError *error = NULL;
- debug_printf (1, "test_sniffing(\"%s\", \"%s\")\n", path, expected_type);
+ uri = soup_uri_new_with_base (base_uri, path);
+ msg = soup_message_new_from_uri ("GET", uri);
g_signal_connect (msg, "content-sniffed",
G_CALLBACK (sniffing_content_sniffed), &sniffed_type);
- g_object_ref (msg);
-
- soup_session_queue_message (session, msg, finished, loop);
-
- g_main_loop_run (loop);
+ soup_session_send_message (session, msg);
+ g_assert_cmpstr (sniffed_type, ==, expected_type);
+ g_free (sniffed_type);
+ g_object_unref (msg);
- if (!sniffed_type) {
- debug_printf (1, " message was not sniffed!\n");
- errors++;
- } else if (strcmp (sniffed_type, expected_type) != 0) {
- debug_printf (1, " sniffing failed! expected %s, got %s\n",
- expected_type, sniffed_type);
- errors++;
+ req = soup_session_request_uri (session, uri, NULL);
+ stream = soup_test_request_send (req, NULL, 0, &error);
+ if (stream) {
+ soup_test_request_close_stream (req, stream, NULL, &error);
+ g_object_unref (stream);
}
- g_free (sniffed_type);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+
+ req_sniffed_type = soup_request_get_content_type (req);
+ g_assert_cmpstr (req_sniffed_type, ==, expected_type);
+ g_object_unref (req);
soup_uri_free (uri);
- g_object_unref (msg);
- g_main_loop_unref (loop);
}
static void
-test_disabled (const char *path)
+do_sniffing_test (gconstpointer data)
{
- SoupURI *uri = soup_uri_new_with_base (base_uri, path);
- SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
- GMainLoop *loop = g_main_loop_new (NULL, TRUE);
+ const char *path_and_result = data;
+ char **parts;
+
+ parts = g_strsplit (path_and_result, " => ", -1);
+ g_assert (parts && parts[0] && parts[1] && !parts[2]);
+
+ test_sniffing (parts[0], parts[1]);
+ g_strfreev (parts);
+}
+
+static void
+test_disabled (gconstpointer data)
+{
+ const char *path = data;
+ SoupURI *uri;
+ SoupMessage *msg;
+ SoupRequest *req;
+ GInputStream *stream;
char *sniffed_type = NULL;
+ const char *sniffed_content_type;
+ GError *error = NULL;
- soup_message_disable_feature (msg, SOUP_TYPE_CONTENT_SNIFFER);
+ g_test_bug ("574773");
- debug_printf (1, "test_disabled(\"%s\")\n", path);
+ uri = soup_uri_new_with_base (base_uri, path);
+
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_message_disable_feature (msg, SOUP_TYPE_CONTENT_SNIFFER);
g_signal_connect (msg, "content-sniffed",
G_CALLBACK (sniffing_content_sniffed), &sniffed_type);
- g_object_ref (msg);
-
- soup_session_queue_message (session, msg, finished, loop);
+ soup_session_send_message (session, msg);
- g_main_loop_run (loop);
+ g_assert_null (sniffed_type);
+ g_object_unref (msg);
- if (sniffed_type) {
- debug_printf (1, " message was sniffed!\n");
- errors++;
- g_free (sniffed_type);
+ req = soup_session_request_uri (session, uri, NULL);
+ msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
+ soup_message_disable_feature (msg, SOUP_TYPE_CONTENT_SNIFFER);
+ g_object_unref (msg);
+ stream = soup_test_request_send (req, NULL, 0, &error);
+ if (stream) {
+ soup_test_request_close_stream (req, stream, NULL, &error);
+ g_object_unref (stream);
}
+ g_assert_no_error (error);
+
+ sniffed_content_type = soup_request_get_content_type (req);
+ g_assert_cmpstr (sniffed_content_type, ==, NULL);
+
+ g_object_unref (req);
soup_uri_free (uri);
- g_object_unref (msg);
- g_main_loop_unref (loop);
}
int
main (int argc, char **argv)
{
SoupServer *server;
+ int ret;
test_init (argc, argv, NULL);
@@ -451,112 +445,171 @@ main (int argc, char **argv)
base_uri = soup_uri_new ("http://127.0.0.1/");
soup_uri_set_port (base_uri, soup_server_get_port (server));
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
-
- /* No sniffer, no content_sniffed should be emitted */
- do_signals_test (FALSE, FALSE, FALSE, FALSE, FALSE);
- do_signals_test (FALSE, FALSE, FALSE, TRUE, FALSE);
- do_signals_test (FALSE, FALSE, TRUE, FALSE, FALSE);
- do_signals_test (FALSE, FALSE, TRUE, TRUE, FALSE);
-
- do_signals_test (FALSE, TRUE, TRUE, FALSE, FALSE);
- do_signals_test (FALSE, TRUE, TRUE, TRUE, FALSE);
- do_signals_test (FALSE, TRUE, FALSE, FALSE, FALSE);
- do_signals_test (FALSE, TRUE, FALSE, TRUE, FALSE);
-
- /* Tests that the signals are correctly emitted for empty
- * responses; see
- * http://bugzilla.gnome.org/show_bug.cgi?id=587907 */
-
- do_signals_test (FALSE, TRUE, TRUE, FALSE, TRUE);
- do_signals_test (FALSE, TRUE, TRUE, TRUE, TRUE);
-
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
- /* Now, with a sniffer, content_sniffed must be emitted after
- * got-headers, and before got-chunk.
- */
- do_signals_test (TRUE, FALSE, FALSE, FALSE, FALSE);
- do_signals_test (TRUE, FALSE, FALSE, TRUE, FALSE);
- do_signals_test (TRUE, FALSE, TRUE, FALSE, FALSE);
- do_signals_test (TRUE, FALSE, TRUE, TRUE, FALSE);
-
- do_signals_test (TRUE, TRUE, TRUE, FALSE, FALSE);
- do_signals_test (TRUE, TRUE, TRUE, TRUE, FALSE);
- do_signals_test (TRUE, TRUE, FALSE, FALSE, FALSE);
- do_signals_test (TRUE, TRUE, FALSE, TRUE, FALSE);
-
- /* Empty response tests */
- do_signals_test (TRUE, TRUE, TRUE, FALSE, TRUE);
- do_signals_test (TRUE, TRUE, TRUE, TRUE, TRUE);
-
- /* Test the text_or_binary sniffing path */
+ g_test_add_data_func ("/sniffing/signals/no-sniffer",
+ GINT_TO_POINTER (FALSE),
+ do_signals_tests);
+ g_test_add_data_func ("/sniffing/signals/with-sniffer",
+ GINT_TO_POINTER (TRUE),
+ do_signals_tests);
+
+ /* Test the apache bug sniffing path */
+ g_test_add_data_func ("/sniffing/apache-bug/binary",
+ "/apache_bug/text_binary.txt => application/octet-stream",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/apache-bug/text",
+ "/apache_bug/text.txt => text/plain",
+ do_sniffing_test);
+
+ /* X-Content-Type-Options: nosniff */
+ g_test_add_data_func ("/sniffing/nosniff",
+ "nosniff/home.gif => no/sniffing-allowed",
+ do_sniffing_test);
/* GIF is a 'safe' type */
- test_sniffing ("/text_or_binary/home.gif", "image/gif");
+ g_test_add_data_func ("/sniffing/type/gif",
+ "text_or_binary/home.gif => image/gif",
+ do_sniffing_test);
/* With our current code, no sniffing is done using GIO, so
* the mbox will be identified as text/plain; should we change
* this?
*/
- test_sniffing ("/text_or_binary/mbox", "text/plain");
+ g_test_add_data_func ("/sniffing/type/mbox",
+ "text_or_binary/mbox => text/plain",
+ do_sniffing_test);
/* HTML is considered unsafe for this algorithm, since it is
* scriptable, so going from text/plain to text/html is
* considered 'privilege escalation'
*/
- test_sniffing ("/text_or_binary/test.html", "text/plain");
+ g_test_add_data_func ("/sniffing/type/html-in-text-context",
+ "text_or_binary/test.html => text/plain",
+ do_sniffing_test);
/* text/plain with binary content and unknown pattern should be
- * application/octet-stream */
- test_sniffing ("/text_or_binary/text_binary.txt", "application/octet-stream");
+ * application/octet-stream
+ */
+ g_test_add_data_func ("/sniffing/type/text-binary",
+ "text_or_binary/text_binary.txt => application/octet-stream",
+ do_sniffing_test);
- /* text/plain with binary content and scriptable pattern should be
- * application/octet-stream to avoid 'privilege escalation' */
- test_sniffing ("/text_or_binary/html_binary.html", "application/octet-stream");
+ /* text/html with binary content and scriptable pattern should be
+ * application/octet-stream to avoid 'privilege escalation'
+ */
+ g_test_add_data_func ("/sniffing/type/html-binary",
+ "text_or_binary/html_binary.html => application/octet-stream",
+ do_sniffing_test);
/* text/plain with binary content and non scriptable known pattern should
- * be the given type */
- test_sniffing ("/text_or_binary/ps_binary.ps", "application/postscript");
+ * be the given type
+ */
+ g_test_add_data_func ("/sniffing/type/ps",
+ "text_or_binary/ps_binary.ps => application/postscript",
+ do_sniffing_test);
/* Test the unknown sniffing path */
-
- test_sniffing ("/unknown/test.html", "text/html");
- test_sniffing ("/unknown/home.gif", "image/gif");
- test_sniffing ("/unknown/mbox", "text/plain");
- test_sniffing ("/unknown/text_binary.txt", "application/octet-stream");
+ g_test_add_data_func ("/sniffing/type/unknown-html",
+ "unknown/test.html => text/html",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/unknown-gif",
+ "unknown/home.gif => image/gif",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/unknown-mbox",
+ "unknown/mbox => text/plain",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/unknown-binary",
+ "unknown/text_binary.txt => application/octet-stream",
+ do_sniffing_test);
+ /* FIXME g_test_bug ("715126") */
+ g_test_add_data_func ("/sniffing/type/unknown-leading-space",
+ "unknown/leading_space.html => text/html",
+ do_sniffing_test);
/* Test the XML sniffing path */
-
- test_sniffing ("/type/text_xml/home.gif", "text/xml");
- test_sniffing ("/type/anice_type+xml/home.gif", "anice/type+xml");
- test_sniffing ("/type/application_xml/home.gif", "application/xml");
-
- /* Test the image sniffing path */
-
- test_sniffing ("/type/image_png/home.gif", "image/gif");
+ g_test_add_data_func ("/sniffing/type/xml",
+ "type/text_xml/home.gif => text/xml",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/xml+xml",
+ "type/anice_type+xml/home.gif => anice/type+xml",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/application-xml",
+ "type/application_xml/home.gif => application/xml",
+ do_sniffing_test);
/* Test the feed or html path */
+ g_test_add_data_func ("/sniffing/type/html/html",
+ "type/text_html/test.html => text/html",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/html/rss",
+ "type/text_html/rss20.xml => application/rss+xml",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/html/atom",
+ "type/text_html/atom.xml => application/atom+xml",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/html/rdf",
+ "type/text_html/feed.rdf => application/rss+xml",
+ do_sniffing_test);
- test_sniffing ("/type/text_html/test.html", "text/html");
- test_sniffing ("/type/text_html/rss20.xml", "application/rss+xml");
- test_sniffing ("/type/text_html/atom.xml", "application/atom+xml");
+ /* Test the image sniffing path */
+ g_test_add_data_func ("/sniffing/type/image/gif",
+ "type/image_png/home.gif => image/gif",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/image/png",
+ "type/image_gif/home.png => image/png",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/image/jpeg",
+ "type/image_png/home.jpg => image/jpeg",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/image/webp",
+ "type/image_png/tux.webp => image/webp",
+ do_sniffing_test);
+
+ /* Test audio and video sniffing path */
+ g_test_add_data_func ("/sniffing/type/audio/wav",
+ "type/audio_mpeg/test.wav => audio/wave",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/audio/aiff",
+ "type/audio_mpeg/test.aiff => audio/aiff",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/audio/ogg",
+ "type/audio_mpeg/test.ogg => application/ogg",
+ do_sniffing_test);
+ g_test_add_data_func ("/sniffing/type/video/webm",
+ "type/video_theora/test.webm => video/webm",
+ do_sniffing_test);
+
+ /* Test the MP4 sniffing path */
+ g_test_add_data_func ("/sniffing/type/video/mp4",
+ "unknown/test.mp4 => video/mp4",
+ do_sniffing_test);
/* The spec tells us to only use the last Content-Type header */
-
- test_sniffing ("/multiple_headers/home.gif", "image/gif");
+ g_test_add_data_func ("/sniffing/multiple-headers",
+ "multiple_headers/home.gif => image/gif",
+ do_sniffing_test);
/* Test that we keep the parameters when sniffing */
- test_sniffing ("/type/text_html; charset=UTF-8/test.html", "text/html; charset=UTF-8");
+ g_test_add_data_func ("/sniffing/parameters",
+ "type/text_html; charset=UTF-8/test.html => text/html; charset=UTF-8",
+ do_sniffing_test);
/* Test that disabling the sniffer works correctly */
+ g_test_add_data_func ("/sniffing/disabled",
+ "/text_or_binary/home.gif",
+ test_disabled);
- test_disabled ("/text_or_binary/home.gif");
+ ret = g_test_run ();
soup_uri_free (base_uri);
soup_test_session_abort_unref (session);
soup_test_server_quit_unref (server);
+
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/socket-test.c b/tests/socket-test.c
new file mode 100644
index 00000000..5bcc3b0c
--- /dev/null
+++ b/tests/socket-test.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2007-2012 Red Hat, Inc.
+ * Copyright 2012 Nokia Corporation
+ */
+
+#include "test-utils.h"
+
+#include <gio/gnetworking.h>
+
+static void
+do_unconnected_socket_test (void)
+{
+ SoupAddress *localhost;
+ SoupSocket *sock;
+ SoupSocket *client;
+ SoupAddress *addr;
+ guint res;
+ struct sockaddr_in in_localhost;
+
+ g_test_bug ("673083");
+
+ in_localhost.sin_family = AF_INET;
+ in_localhost.sin_port = 0;
+ in_localhost.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ localhost = soup_address_new_from_sockaddr (
+ (struct sockaddr *) &in_localhost, sizeof (in_localhost));
+ g_assert_true (localhost != NULL);
+ res = soup_address_resolve_sync (localhost, NULL);
+ g_assert_cmpuint (res, ==, SOUP_STATUS_OK);
+
+ sock = soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, localhost,
+ NULL);
+ g_assert_true (sock != NULL);
+
+ addr = soup_socket_get_local_address (sock);
+ g_assert_true (addr != NULL);
+ g_assert_cmpstr (soup_address_get_physical (addr), ==, "127.0.0.1");
+ g_assert_cmpuint (soup_address_get_port (addr), ==, 0);
+
+ /* fails with ENOTCONN */
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*socket not connected*");
+ addr = soup_socket_get_remote_address (sock);
+ g_test_assert_expected_messages ();
+ g_assert_null (addr);
+
+ res = soup_socket_listen (sock);
+ g_assert_true (res);
+
+ addr = soup_socket_get_local_address (sock);
+ g_assert_true (addr != NULL);
+ g_assert_cmpstr (soup_address_get_physical (addr), ==, "127.0.0.1");
+ g_assert_cmpuint (soup_address_get_port (addr), >, 0);
+
+ client = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS,
+ soup_socket_get_local_address (sock),
+ NULL);
+ res = soup_socket_connect_sync (client, NULL);
+ g_assert_cmpuint (res, ==, SOUP_STATUS_OK);
+ addr = soup_socket_get_local_address (client);
+ g_assert_true (addr != NULL);
+ addr = soup_socket_get_remote_address (client);
+ g_assert_true (addr != NULL);
+ g_assert_cmpstr (soup_address_get_physical (addr), ==, "127.0.0.1");
+ g_assert_cmpuint (soup_address_get_port (addr), >, 0);
+ g_object_unref (client);
+
+ client = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS,
+ soup_socket_get_local_address (sock),
+ NULL);
+ /* save it for later */
+
+ /* listening socket fails with ENOTCONN */
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*endpoint is not connected*");
+ addr = soup_socket_get_remote_address (sock);
+ g_test_assert_expected_messages ();
+ g_assert_null (addr);
+
+ soup_socket_disconnect (sock);
+
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*socket not connected*");
+ addr = soup_socket_get_remote_address (sock);
+ g_test_assert_expected_messages ();
+ g_assert_null (addr);
+
+ /* has never been connected */
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*socket not connected*");
+ addr = soup_socket_get_local_address (client);
+ g_test_assert_expected_messages ();
+ g_assert_null (addr);
+
+ res = soup_socket_connect_sync (client, NULL);
+ g_assert_cmpuint (res, ==, SOUP_STATUS_CANT_CONNECT);
+
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*socket not connected*");
+ addr = soup_socket_get_local_address (client);
+ g_test_assert_expected_messages ();
+ g_assert_null (addr);
+
+ g_object_unref (localhost);
+ g_object_unref (client);
+ g_object_unref (sock);
+}
+
+int
+main (int argc, char **argv)
+{
+ int ret;
+
+ test_init (argc, argv, NULL);
+
+ g_test_add_func ("/sockets/unconnected", do_unconnected_socket_test);
+
+ ret = g_test_run ();
+
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/soup-tests.gresource.xml b/tests/soup-tests.gresource.xml
new file mode 100644
index 00000000..b24a7297
--- /dev/null
+++ b/tests/soup-tests.gresource.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/libsoup/tests">
+ <file>index.txt</file>
+ <file>resources/atom.xml</file>
+ <file>resources/feed.rdf</file>
+ <file>resources/home.gif</file>
+ <file>resources/home.jpg</file>
+ <file>resources/home.png</file>
+ <file>resources/html_binary.html</file>
+ <file>resources/leading_space.html</file>
+ <file>resources/mbox</file>
+ <file>resources/mbox.gz</file>
+ <file>resources/mbox.raw</file>
+ <file>resources/mbox.zlib</file>
+ <file>resources/ps_binary.ps</file>
+ <file>resources/rss20.xml</file>
+ <file>resources/test.aiff</file>
+ <file>resources/test.html</file>
+ <file>resources/test.mp4</file>
+ <file>resources/test.ogg</file>
+ <file>resources/test.wav</file>
+ <file>resources/test.webm</file>
+ <file>resources/text.txt</file>
+ <file>resources/text_binary.txt</file>
+ <file>resources/tux.webp</file>
+ </gresource>
+</gresources>
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
index 6d96bc79..e6bbb615 100644
--- a/tests/ssl-test.c
+++ b/tests/ssl-test.c
@@ -2,8 +2,10 @@
#include "test-utils.h"
+static char *uri;
+
static void
-do_properties_test_for_session (SoupSession *session, char *uri)
+do_properties_test_for_session (SoupSession *session, const char *uri)
{
SoupMessage *msg;
GTlsCertificate *cert;
@@ -11,42 +13,27 @@ do_properties_test_for_session (SoupSession *session, char *uri)
msg = soup_message_new ("GET", uri);
soup_session_send_message (session, msg);
- if (msg->status_code != SOUP_STATUS_OK) {
- debug_printf (1, " FAILED: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
if (soup_message_get_https_status (msg, &cert, &flags)) {
- if (!G_IS_TLS_CERTIFICATE (cert)) {
- debug_printf (1, " No certificate?\n");
- errors++;
- }
- if (flags != G_TLS_CERTIFICATE_UNKNOWN_CA) {
- debug_printf (1, " Wrong cert flags (got %x, wanted %x)\n",
- flags, G_TLS_CERTIFICATE_UNKNOWN_CA);
- errors++;
- }
- } else {
- debug_printf (1, " Response not https\n");
- errors++;
- }
- if (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED) {
- debug_printf (1, " CERTIFICATE_TRUSTED set?\n");
- errors++;
- }
+ g_assert_true (G_IS_TLS_CERTIFICATE (cert));
+ g_assert_cmpuint (flags, ==, G_TLS_CERTIFICATE_UNKNOWN_CA);
+ } else
+ soup_test_assert (FALSE, "Response not https");
+
+ g_test_bug ("665182");
+ g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
g_object_unref (msg);
}
static void
-do_properties_tests (char *uri)
+do_async_properties_tests (void)
{
SoupSession *session;
- debug_printf (1, "\nSoupMessage properties\n");
+ SOUP_TEST_SKIP_IF_NO_TLS;
- debug_printf (1, " async\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
g_object_set (G_OBJECT (session),
SOUP_SESSION_SSL_CA_FILE, "/dev/null",
@@ -54,8 +41,15 @@ do_properties_tests (char *uri)
NULL);
do_properties_test_for_session (session, uri);
soup_test_session_abort_unref (session);
+}
+
+static void
+do_sync_properties_tests (void)
+{
+ SoupSession *session;
+
+ SOUP_TEST_SKIP_IF_NO_TLS;
- debug_printf (1, " sync\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
g_object_set (G_OBJECT (session),
SOUP_SESSION_SSL_CA_FILE, "/dev/null",
@@ -65,81 +59,74 @@ do_properties_tests (char *uri)
soup_test_session_abort_unref (session);
}
+typedef struct {
+ const char *name;
+ gboolean sync;
+ gboolean strict;
+ gboolean with_ca_list;
+ guint expected_status;
+} StrictnessTest;
+
+static const StrictnessTest strictness_tests[] = {
+ { "/ssl/strictness/async/strict/with-ca",
+ FALSE, TRUE, TRUE, SOUP_STATUS_OK },
+ { "/ssl/strictness/async/strict/without-ca",
+ FALSE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED },
+ { "/ssl/strictness/async/non-strict/with-ca",
+ FALSE, FALSE, TRUE, SOUP_STATUS_OK },
+ { "/ssl/strictness/async/non-strict/without-ca",
+ FALSE, FALSE, FALSE, SOUP_STATUS_OK },
+ { "/ssl/strictness/sync/strict/with-ca",
+ TRUE, TRUE, TRUE, SOUP_STATUS_OK },
+ { "/ssl/strictness/sync/strict/without-ca",
+ TRUE, TRUE, FALSE, SOUP_STATUS_SSL_FAILED },
+ { "/ssl/strictness/sync/non-strict/with-ca",
+ TRUE, FALSE, TRUE, SOUP_STATUS_OK },
+ { "/ssl/strictness/sync/non-strict/without-ca",
+ TRUE, FALSE, FALSE, SOUP_STATUS_OK },
+};
+
static void
-do_one_strict_test (SoupSession *session, char *uri,
- gboolean strict, gboolean with_ca_list,
- guint expected_status)
+do_strictness_test (gconstpointer data)
{
+ const StrictnessTest *test = data;
+ SoupSession *session;
SoupMessage *msg;
+ GTlsCertificateFlags flags = 0;
- /* Note that soup_test_session_new() sets
- * SOUP_SESSION_SSL_CA_FILE by default, and turns off
- * SOUP_SESSION_SSL_STRICT.
- */
+ SOUP_TEST_SKIP_IF_NO_TLS;
- g_object_set (G_OBJECT (session),
- SOUP_SESSION_SSL_STRICT, strict,
- SOUP_SESSION_SSL_CA_FILE, with_ca_list ? SRCDIR "/test-cert.pem" : "/dev/null",
- NULL);
- /* Close existing connections with old params */
- soup_session_abort (session);
+ session = soup_test_session_new (test->sync ? SOUP_TYPE_SESSION_SYNC : SOUP_TYPE_SESSION_ASYNC,
+ NULL);
+ if (!test->strict) {
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_SSL_STRICT, FALSE,
+ NULL);
+ }
+ if (!test->with_ca_list) {
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_SSL_CA_FILE, "/dev/null",
+ NULL);
+ }
msg = soup_message_new ("GET", uri);
soup_session_send_message (session, msg);
- if (msg->status_code != expected_status) {
- debug_printf (1, " FAILED: %d %s (expected %d %s)\n",
- msg->status_code, msg->reason_phrase,
- expected_status,
- soup_status_get_phrase (expected_status));
- if (msg->status_code == SOUP_STATUS_SSL_FAILED) {
- GTlsCertificateFlags flags = 0;
-
- soup_message_get_https_status (msg, NULL, &flags);
- debug_printf (1, " tls error flags: 0x%x\n", flags);
- }
- errors++;
- } else if (with_ca_list && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED)) {
- debug_printf (1, " CERTIFICATE_TRUSTED not set?\n");
- errors++;
- }
- } else {
- if (with_ca_list && soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED) {
- debug_printf (1, " CERTIFICATE_TRUSTED set?\n");
- errors++;
- }
- }
-
- g_object_unref (msg);
-}
+ soup_test_assert_message_status (msg, test->expected_status);
-static void
-do_strict_tests (char *uri)
-{
- SoupSession *session;
+ g_test_bug ("690176");
+ g_assert_true (soup_message_get_https_status (msg, NULL, &flags));
- debug_printf (1, "\nstrict/nonstrict\n");
+ g_test_bug ("665182");
+ if (test->with_ca_list && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ g_assert_true (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
+ else
+ g_assert_false (soup_message_get_flags (msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED);
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- debug_printf (1, " async with CA list\n");
- do_one_strict_test (session, uri, TRUE, TRUE, SOUP_STATUS_OK);
- debug_printf (1, " async without CA list\n");
- do_one_strict_test (session, uri, TRUE, FALSE, SOUP_STATUS_SSL_FAILED);
- debug_printf (1, " async non-strict with CA list\n");
- do_one_strict_test (session, uri, FALSE, TRUE, SOUP_STATUS_OK);
- debug_printf (1, " async non-strict without CA list\n");
- do_one_strict_test (session, uri, FALSE, FALSE, SOUP_STATUS_OK);
- soup_test_session_abort_unref (session);
+ if (msg->status_code == SOUP_STATUS_SSL_FAILED &&
+ test->expected_status != SOUP_STATUS_SSL_FAILED)
+ debug_printf (1, " tls error flags: 0x%x\n", flags);
- session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
- debug_printf (1, " sync with CA list\n");
- do_one_strict_test (session, uri, TRUE, TRUE, SOUP_STATUS_OK);
- debug_printf (1, " sync without CA list\n");
- do_one_strict_test (session, uri, TRUE, FALSE, SOUP_STATUS_SSL_FAILED);
- debug_printf (1, " sync non-strict with CA list\n");
- do_one_strict_test (session, uri, FALSE, TRUE, SOUP_STATUS_OK);
- debug_printf (1, " sync non-strict without CA list\n");
- do_one_strict_test (session, uri, FALSE, FALSE, SOUP_STATUS_OK);
+ g_object_unref (msg);
soup_test_session_abort_unref (session);
}
@@ -160,7 +147,9 @@ do_session_property_tests (void)
char *ca_file;
SoupSession *session;
- debug_printf (1, "session properties\n");
+ g_test_bug ("673678");
+
+ SOUP_TEST_SKIP_IF_NO_TLS;
session = soup_session_async_new ();
g_signal_connect (session, "notify::ssl-use-system-ca-file",
@@ -175,20 +164,12 @@ do_session_property_tests (void)
"tls-database", &tlsdb,
"ssl-ca-file", &ca_file,
NULL);
- if (use_system) {
- debug_printf (1, " ssl-use-system-ca-file defaults to TRUE?\n");
- errors++;
- }
- if (tlsdb) {
- debug_printf (1, " tls-database set by default?\n");
- errors++;
- g_object_unref (tlsdb);
- }
- if (ca_file) {
- debug_printf (1, " ca-file set by default?\n");
- errors++;
- g_free (ca_file);
- }
+ soup_test_assert (!use_system,
+ "ssl-use-system-ca-file defaults to TRUE");
+ soup_test_assert (tlsdb == NULL,
+ "tls-database set by default");
+ soup_test_assert (ca_file == NULL,
+ "ca-file set by default");
use_system_changed = tlsdb_changed = ca_file_changed = FALSE;
g_object_set (G_OBJECT (session),
@@ -199,68 +180,37 @@ do_session_property_tests (void)
"tls-database", &tlsdb,
"ssl-ca-file", &ca_file,
NULL);
- if (!use_system) {
- debug_printf (1, " setting ssl-use-system-ca-file failed\n");
- errors++;
- }
- if (!tlsdb) {
- debug_printf (1, " setting ssl-use-system-ca-file didn't set tls-database\n");
- errors++;
- } else
- g_object_unref (tlsdb);
- if (ca_file) {
- debug_printf (1, " setting ssl-use-system-ca-file set ssl-ca-file\n");
- errors++;
- g_free (ca_file);
- }
- if (!use_system_changed) {
- debug_printf (1, " setting ssl-use-system-ca-file didn't emit notify::ssl-use-system-ca-file\n");
- errors++;
- }
- if (!tlsdb_changed) {
- debug_printf (1, " setting ssl-use-system-ca-file didn't emit notify::tls-database\n");
- errors++;
- }
- if (ca_file_changed) {
- debug_printf (1, " setting ssl-use-system-ca-file emitted notify::ssl-ca-file\n");
- errors++;
- }
+ soup_test_assert (use_system,
+ "setting ssl-use-system-ca-file failed");
+ g_assert_true (use_system_changed);
+ soup_test_assert (tlsdb != NULL,
+ "setting ssl-use-system-ca-file didn't set tls-database");
+ g_assert_true (tlsdb_changed);
+ g_clear_object (&tlsdb);
+ soup_test_assert (ca_file == NULL,
+ "setting ssl-use-system-ca-file set ssl-ca-file");
+ g_assert_false (ca_file_changed);
use_system_changed = tlsdb_changed = ca_file_changed = FALSE;
g_object_set (G_OBJECT (session),
- "ssl-ca-file", SRCDIR "/test-cert.pem",
+ "ssl-ca-file", g_test_get_filename (G_TEST_DIST, "/test-cert.pem", NULL),
NULL);
g_object_get (G_OBJECT (session),
"ssl-use-system-ca-file", &use_system,
"tls-database", &tlsdb,
"ssl-ca-file", &ca_file,
NULL);
- if (use_system) {
- debug_printf (1, " setting ssl-ca-file left ssl-use-system-ca-file set\n");
- errors++;
- }
- if (!tlsdb) {
- debug_printf (1, " setting ssl-ca-file didn't set tls-database\n");
- errors++;
- } else
- g_object_unref (tlsdb);
- if (!ca_file) {
- debug_printf (1, " setting ssl-ca-file failed\n");
- errors++;
- } else
- g_free (ca_file);
- if (!use_system_changed) {
- debug_printf (1, " setting ssl-ca-file didn't emit notify::ssl-use-system-ca-file\n");
- errors++;
- }
- if (!tlsdb_changed) {
- debug_printf (1, " setting ssl-ca-file didn't emit notify::tls-database\n");
- errors++;
- }
- if (!ca_file_changed) {
- debug_printf (1, " setting ssl-ca-file didn't emit notify::ssl-ca-file\n");
- errors++;
- }
+ soup_test_assert (!use_system,
+ "setting ssl-ca-file left ssl-use-system-ca-file set");
+ g_assert_true (use_system_changed);
+ soup_test_assert (tlsdb != NULL,
+ "setting ssl-ca-file didn't set tls-database");
+ g_assert_true (tlsdb_changed);
+ g_clear_object (&tlsdb);
+ soup_test_assert (ca_file != NULL,
+ "setting ssl-ca-file failed");
+ g_assert_true (ca_file_changed);
+ g_free (ca_file);
use_system_changed = tlsdb_changed = ca_file_changed = FALSE;
g_object_set (G_OBJECT (session),
@@ -271,32 +221,15 @@ do_session_property_tests (void)
"tls-database", &tlsdb,
"ssl-ca-file", &ca_file,
NULL);
- if (use_system) {
- debug_printf (1, " setting tls-database NULL left ssl-use-system-ca-file set\n");
- errors++;
- }
- if (tlsdb) {
- debug_printf (1, " setting tls-database NULL failed\n");
- errors++;
- g_object_unref (tlsdb);
- }
- if (ca_file) {
- debug_printf (1, " setting tls-database didn't clear ssl-ca-file\n");
- errors++;
- g_free (ca_file);
- }
- if (use_system_changed) {
- debug_printf (1, " setting tls-database emitted notify::ssl-use-system-ca-file\n");
- errors++;
- }
- if (!tlsdb_changed) {
- debug_printf (1, " setting tls-database didn't emit notify::tls-database\n");
- errors++;
- }
- if (!ca_file_changed) {
- debug_printf (1, " setting tls-database didn't emit notify::ssl-ca-file\n");
- errors++;
- }
+ soup_test_assert (!use_system,
+ "setting tls-database NULL left ssl-use-system-ca-file set");
+ g_assert_false (use_system_changed);
+ soup_test_assert (tlsdb == NULL,
+ "setting tls-database NULL failed");
+ g_assert_true (tlsdb_changed);
+ soup_test_assert (ca_file == NULL,
+ "setting tls-database didn't clear ssl-ca-file");
+ g_assert_true (ca_file_changed);
soup_test_session_abort_unref (session);
}
@@ -319,7 +252,7 @@ int
main (int argc, char **argv)
{
SoupServer *server;
- char *uri;
+ int i, ret;
test_init (argc, argv, NULL);
@@ -328,15 +261,25 @@ main (int argc, char **argv)
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));
+ }
- do_session_property_tests ();
- do_strict_tests (uri);
- do_properties_tests (uri);
+ 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);
+ for (i = 0; i < G_N_ELEMENTS (strictness_tests); i++) {
+ g_test_add_data_func (strictness_tests[i].name,
+ &strictness_tests[i],
+ do_strictness_test);
+ }
+
+ ret = g_test_run ();
+
+ if (tls_available) {
g_free (uri);
soup_test_server_quit_unref (server);
}
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/streaming-test.c b/tests/streaming-test.c
index 239e0ce8..8d8c02ef 100644
--- a/tests/streaming-test.c
+++ b/tests/streaming-test.c
@@ -7,28 +7,8 @@
#define RESPONSE_CHUNK_SIZE 1024
-char *full_response, *full_response_md5;
-gsize full_response_length;
-
-static void
-get_full_response (void)
-{
- GError *error = NULL;
-
- if (!g_file_get_contents (SRCDIR "/index.txt",
- &full_response,
- &full_response_length,
- &error)) {
- g_printerr ("Could not read index file %s: %s\n",
- SRCDIR "/index.txt", error->message);
- g_error_free (error);
- exit (1);
- }
-
- full_response_md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
- (guchar *)full_response,
- full_response_length);
-}
+SoupBuffer *full_response;
+char *full_response_md5;
static void
write_next_chunk (SoupMessage *msg, gpointer user_data)
@@ -36,12 +16,12 @@ write_next_chunk (SoupMessage *msg, gpointer user_data)
gsize *offset = user_data;
gsize chunk_length;
- chunk_length = MIN (RESPONSE_CHUNK_SIZE, full_response_length - *offset);
+ chunk_length = MIN (RESPONSE_CHUNK_SIZE, full_response->length - *offset);
if (chunk_length > 0) {
debug_printf (2, " writing chunk\n");
soup_message_body_append (msg->response_body,
SOUP_MEMORY_STATIC,
- full_response + *offset,
+ full_response->data + *offset,
chunk_length);
*offset += chunk_length;
} else {
@@ -74,7 +54,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
soup_message_headers_set_encoding (msg->response_headers,
SOUP_ENCODING_CONTENT_LENGTH);
soup_message_headers_set_content_length (msg->response_headers,
- full_response_length);
+ full_response->length);
} else if (!strcmp (path, "/eof")) {
soup_message_headers_set_encoding (msg->response_headers,
SOUP_ENCODING_EOF);
@@ -106,44 +86,49 @@ do_request (SoupSession *session, SoupURI *base_uri, char *path)
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, " message failed: %d %s\n",
- msg->status_code, msg->reason_phrase);
- errors++;
- }
-
- if (msg->response_body->length != full_response_length) {
- debug_printf (1, " received length mismatch: expected %d, got %d\n",
- (int)full_response_length, (int)msg->request_body->length);
- errors++;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_assert_cmpint (msg->response_body->length, ==, full_response->length);
md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
(guchar *)msg->response_body->data,
msg->response_body->length);
- if (strcmp (md5, full_response_md5) != 0) {
- debug_printf (1, " data mismatch: expected %s, got %s\n",
- full_response_md5, md5);
- errors++;
- }
+ g_assert_cmpstr (md5, ==, full_response_md5);
g_free (md5);
g_object_unref (msg);
}
static void
-do_tests (SoupURI *base_uri)
+do_chunked_test (gconstpointer data)
{
+ SoupURI *base_uri = (SoupURI *)data;
SoupSession *session;
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- debug_printf (1, "Chunked encoding\n");
do_request (session, base_uri, "chunked");
- debug_printf (1, "\n");
- debug_printf (1, "Content-Length encoding\n");
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_content_length_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
do_request (session, base_uri, "content-length");
- debug_printf (1, "\n");
- debug_printf (1, "EOF encoding\n");
+ soup_test_session_abort_unref (session);
+}
+
+static void
+do_eof_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+
+ g_test_bug ("572153");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
do_request (session, base_uri, "eof");
soup_test_session_abort_unref (session);
}
@@ -155,9 +140,14 @@ main (int argc, char **argv)
SoupServer *server;
guint port;
SoupURI *base_uri;
+ int ret;
test_init (argc, argv, NULL);
- get_full_response ();
+
+ full_response = soup_test_get_index ();
+ full_response_md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+ (guchar *)full_response->data,
+ full_response->length);
server = soup_test_server_new (FALSE);
soup_server_add_handler (server, NULL,
@@ -168,14 +158,19 @@ main (int argc, char **argv)
base_uri = soup_uri_new ("http://127.0.0.1");
soup_uri_set_port (base_uri, port);
- do_tests (base_uri);
- soup_uri_free (base_uri);
+ 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);
+ g_test_add_data_func ("/streaming/eof", base_uri, do_eof_test);
+
+ ret = g_test_run ();
+
+ soup_uri_free (base_uri);
g_main_loop_unref (loop);
- g_free (full_response);
g_free (full_response_md5);
soup_test_server_quit_unref (server);
test_cleanup ();
- return errors != 0;
+
+ return ret;
}
diff --git a/tests/test-utils.c b/tests/test-utils.c
index f040b738..bc160aea 100644
--- a/tests/test-utils.c
+++ b/tests/test-utils.c
@@ -12,8 +12,9 @@ static gboolean apache_running;
#endif
static SoupLogger *logger;
+static SoupBuffer *index_buffer;
-int debug_level, errors;
+int debug_level;
gboolean expect_warning, tls_available;
static int http_debug_level;
@@ -37,7 +38,7 @@ static GOptionEntry debug_entry[] = {
{ "debug", 'd', G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK, increment_debug_level,
"Enable (or increase) test-specific debugging", NULL },
- { "http-debug", 'h', G_OPTION_FLAG_NO_ARG,
+ { "http-debug", 'H', G_OPTION_FLAG_NO_ARG,
G_OPTION_ARG_CALLBACK, increment_http_debug_level,
"Enable (or increase) HTTP-level debugging", NULL },
{ NULL }
@@ -54,21 +55,6 @@ quit (int sig)
exit (1);
}
-static void
-test_log_handler (const char *log_domain, GLogLevelFlags log_level,
- const char *message, gpointer user_data)
-{
- if (log_level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL)) {
- if (expect_warning) {
- expect_warning = FALSE;
- debug_printf (2, "Got expected warning: %s\n", message);
- return;
- } else
- errors++;
- }
- g_log_default_handler (log_domain, log_level, message, user_data);
-}
-
void
test_init (int argc, char **argv, GOptionEntry *entries)
{
@@ -78,7 +64,8 @@ test_init (int argc, char **argv, GOptionEntry *entries)
GTlsBackend *tls_backend;
setlocale (LC_ALL, "");
- g_type_init ();
+ g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+ g_setenv ("GIO_USE_PROXY_RESOLVER", "dummy", TRUE);
name = strrchr (argv[0], '/');
if (!name++)
@@ -87,6 +74,10 @@ test_init (int argc, char **argv, GOptionEntry *entries)
name += 3;
g_set_prgname (name);
+ g_test_init (&argc, &argv, NULL);
+ g_test_set_nonfatal_assertions ();
+ g_test_bug_base ("https://bugzilla.gnome.org/");
+
opts = g_option_context_new (NULL);
g_option_context_add_main_entries (opts, debug_entry, NULL);
if (entries)
@@ -104,8 +95,6 @@ test_init (int argc, char **argv, GOptionEntry *entries)
/* Exit cleanly on ^C in case we're valgrinding. */
signal (SIGINT, quit);
- g_log_set_default_handler (test_log_handler, NULL);
-
tls_backend = g_tls_backend_get_default ();
tls_available = g_tls_backend_supports_tls (tls_backend);
}
@@ -120,16 +109,12 @@ test_cleanup (void)
if (logger)
g_object_unref (logger);
+ if (index_buffer)
+ soup_buffer_free (index_buffer);
g_main_context_unref (g_main_context_default ());
debug_printf (1, "\n");
- if (errors) {
- g_print ("%s: %d error(s).%s\n",
- g_get_prgname (), errors,
- debug_level == 0 ? " Run with '-d' for details" : "");
- } else
- g_print ("%s: OK\n", g_get_prgname ());
}
void
@@ -150,30 +135,52 @@ debug_printf (int level, const char *format, ...)
static gboolean
apache_cmd (const char *cmd)
{
- const char *argv[8];
- char *cwd, *conf;
+ GPtrArray *argv;
+ char *server_root, *cwd, *pid_file;
+#ifdef HAVE_APACHE_2_4
+ char *default_runtime_dir;
+#endif
int status;
gboolean ok;
+ server_root = g_test_build_filename (G_TEST_BUILT, "", NULL);
+
cwd = g_get_current_dir ();
- conf = g_build_filename (cwd, "httpd.conf", NULL);
-
- argv[0] = APACHE_HTTPD;
- argv[1] = "-d";
- argv[2] = cwd;
- argv[3] = "-f";
- argv[4] = conf;
- argv[5] = "-k";
- argv[6] = cmd;
- argv[7] = NULL;
-
- ok = g_spawn_sync (cwd, (char **)argv, NULL, 0, NULL, NULL,
+#ifdef HAVE_APACHE_2_4
+ default_runtime_dir = g_strdup_printf ("DefaultRuntimeDir %s", cwd);
+#endif
+ pid_file = g_strdup_printf ("PidFile %s/httpd.pid", cwd);
+
+ argv = g_ptr_array_new ();
+ g_ptr_array_add (argv, APACHE_HTTPD);
+ g_ptr_array_add (argv, "-d");
+ g_ptr_array_add (argv, server_root);
+ g_ptr_array_add (argv, "-f");
+ g_ptr_array_add (argv, "httpd.conf");
+
+#ifdef HAVE_APACHE_2_4
+ g_ptr_array_add (argv, "-c");
+ g_ptr_array_add (argv, default_runtime_dir);
+#endif
+ g_ptr_array_add (argv, "-c");
+ g_ptr_array_add (argv, pid_file);
+
+ g_ptr_array_add (argv, "-k");
+ g_ptr_array_add (argv, (char *)cmd);
+ g_ptr_array_add (argv, NULL);
+
+ ok = g_spawn_sync (cwd, (char **)argv->pdata, NULL, 0, NULL, NULL,
NULL, NULL, &status, NULL);
if (ok)
ok = (status == 0);
+ g_free (server_root);
g_free (cwd);
- g_free (conf);
+ g_free (pid_file);
+#ifdef HAVE_APACHE_2_4
+ g_free (default_runtime_dir);
+#endif
+ g_ptr_array_free (argv, TRUE);
return ok;
}
@@ -181,6 +188,9 @@ apache_cmd (const char *cmd)
void
apache_init (void)
{
+ if (g_getenv ("SOUP_TESTS_IN_MAKE_CHECK"))
+ return;
+
if (!apache_cmd ("start")) {
g_printerr ("Could not start apache\n");
exit (1);
@@ -218,15 +228,18 @@ soup_test_session_new (GType type, ...)
va_list args;
const char *propname;
SoupSession *session;
+ char *cafile;
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, SRCDIR "/test-cert.pem",
+ SOUP_SESSION_SSL_CA_FILE, cafile,
NULL);
+ g_free (cafile);
if (http_debug_level && !logger) {
SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY);
@@ -243,16 +256,10 @@ soup_test_session_new (GType type, ...)
void
soup_test_session_abort_unref (SoupSession *session)
{
- g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
-
soup_session_abort (session);
- g_object_unref (session);
- if (session) {
- errors++;
- debug_printf (1, "leaked SoupSession!\n");
- g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
- }
+ g_assert_cmpint (G_OBJECT (session)->ref_count, ==, 1);
+ g_object_unref (session);
}
static gpointer run_server_thread (gpointer user_data);
@@ -262,14 +269,14 @@ test_server_new (gboolean in_own_thread, gboolean ssl)
{
SoupServer *server;
GMainContext *async_context;
- const char *ssl_cert_file, *ssl_key_file;
+ char *ssl_cert_file, *ssl_key_file;
SoupAddress *addr;
async_context = in_own_thread ? g_main_context_new () : NULL;
if (ssl) {
- ssl_cert_file = SRCDIR "/test-cert.pem";
- ssl_key_file = SRCDIR "/test-key.pem";
+ 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;
@@ -284,6 +291,8 @@ test_server_new (gboolean in_own_thread, gboolean ssl)
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");
@@ -334,9 +343,6 @@ soup_test_server_quit_unref (SoupServer *server)
{
GThread *thread;
- g_object_add_weak_pointer (G_OBJECT (server),
- (gpointer *)&server);
-
thread = g_object_get_data (G_OBJECT (server), "thread");
if (thread) {
soup_add_completion (soup_server_get_async_context (server),
@@ -344,19 +350,14 @@ soup_test_server_quit_unref (SoupServer *server)
g_thread_join (thread);
} else
soup_server_quit (server);
- g_object_unref (server);
- if (server) {
- errors++;
- debug_printf (1, "leaked SoupServer!\n");
- g_object_remove_weak_pointer (G_OBJECT (server),
- (gpointer *)&server);
- }
+ g_assert_cmpint (G_OBJECT (server)->ref_count, ==, 1);
+ g_object_unref (server);
}
typedef struct {
- GMainLoop *loop;
- GAsyncResult *result;
+ GMainLoop *loop;
+ GAsyncResult *result;
} AsyncAsSyncData;
static void
@@ -364,51 +365,269 @@ async_as_sync_callback (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
- AsyncAsSyncData *data = user_data;
+ AsyncAsSyncData *data = user_data;
+ GMainContext *context;
+
+ data->result = g_object_ref (result);
+ context = g_main_loop_get_context (data->loop);
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+ g_main_loop_quit (data->loop);
+}
+
+typedef struct {
+ SoupRequest *req;
+ GCancellable *cancellable;
+ SoupTestRequestFlags flags;
+} CancelData;
+
+static CancelData *
+create_cancel_data (SoupRequest *req,
+ GCancellable *cancellable,
+ SoupTestRequestFlags flags)
+{
+ CancelData *cancel_data;
+
+ if (!flags)
+ return NULL;
+
+ cancel_data = g_slice_new0 (CancelData);
+ cancel_data->flags = flags;
+ if (flags & SOUP_TEST_REQUEST_CANCEL_MESSAGE && SOUP_IS_REQUEST_HTTP (req))
+ cancel_data->req = g_object_ref (req);
+ else if (flags & SOUP_TEST_REQUEST_CANCEL_CANCELLABLE)
+ cancel_data->cancellable = g_object_ref (cancellable);
+ return cancel_data;
+}
- data->result = g_object_ref (result);
- g_main_loop_quit (data->loop);
+static void inline
+cancel_message_or_cancellable (CancelData *cancel_data)
+{
+ if (cancel_data->flags & SOUP_TEST_REQUEST_CANCEL_MESSAGE) {
+ SoupRequest *req = cancel_data->req;
+ SoupMessage *msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
+ soup_session_cancel_message (soup_request_get_session (req), msg,
+ SOUP_STATUS_CANCELLED);
+ g_object_unref (msg);
+ g_object_unref (req);
+ } else if (cancel_data->flags & SOUP_TEST_REQUEST_CANCEL_CANCELLABLE) {
+ g_cancellable_cancel (cancel_data->cancellable);
+ g_object_unref (cancel_data->cancellable);
+ }
+ g_slice_free (CancelData, cancel_data);
+}
+
+static gboolean
+cancel_request_timeout (gpointer data)
+{
+ cancel_message_or_cancellable ((CancelData *) data);
+ return FALSE;
+}
+
+static gpointer
+cancel_request_thread (gpointer data)
+{
+ g_usleep (100000); /* .1s */
+ cancel_message_or_cancellable ((CancelData *) data);
+ return NULL;
}
GInputStream *
-soup_test_request_send_async_as_sync (SoupRequest *req,
- GCancellable *cancellable,
- GError **error)
+soup_test_request_send (SoupRequest *req,
+ GCancellable *cancellable,
+ guint flags,
+ GError **error)
{
- AsyncAsSyncData data;
- GInputStream *stream;
+ AsyncAsSyncData data;
+ GInputStream *stream;
+ CancelData *cancel_data = create_cancel_data (req, cancellable, flags);
- data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
+ if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req))) {
+ GThread *thread;
- soup_request_send_async (req, cancellable, async_as_sync_callback, &data);
- g_main_loop_run (data.loop);
+ if (cancel_data)
+ thread = g_thread_new ("cancel_request_thread", cancel_request_thread,
+ cancel_data);
+ stream = soup_request_send (req, cancellable, error);
+ if (cancel_data)
+ g_thread_unref (thread);
+ return stream;
+ }
+
+ data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
+ if (cancel_data &&
+ (flags & SOUP_TEST_REQUEST_CANCEL_SOON || flags & SOUP_TEST_REQUEST_CANCEL_IMMEDIATE)) {
+ guint interval = flags & SOUP_TEST_REQUEST_CANCEL_SOON ? 100 : 0;
+ 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);
+ soup_request_send_async (req, cancellable, async_as_sync_callback, &data);
+ g_main_loop_run (data.loop);
- stream = soup_request_send_finish (req, data.result, error);
+ stream = soup_request_send_finish (req, data.result, error);
- g_main_loop_unref (data.loop);
- g_object_unref (data.result);
+ if (cancel_data && (flags & SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH)) {
+ GMainContext *context;
- return stream;
+ cancel_message_or_cancellable (cancel_data);
+
+ context = g_main_loop_get_context (data.loop);
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+ }
+
+ g_main_loop_unref (data.loop);
+ g_object_unref (data.result);
+
+ return stream;
+}
+
+gboolean
+soup_test_request_read_all (SoupRequest *req,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ char buf[8192];
+ AsyncAsSyncData data;
+ gsize nread;
+
+ if (!SOUP_IS_SESSION_SYNC (soup_request_get_session (req)))
+ data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
+
+ do {
+ if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req))) {
+ nread = g_input_stream_read (stream, buf, sizeof (buf),
+ cancellable, error);
+ } else {
+ g_input_stream_read_async (stream, buf, sizeof (buf),
+ G_PRIORITY_DEFAULT, cancellable,
+ async_as_sync_callback, &data);
+ g_main_loop_run (data.loop);
+ nread = g_input_stream_read_finish (stream, data.result, error);
+ g_object_unref (data.result);
+ }
+ } while (nread > 0);
+
+ if (!SOUP_IS_SESSION_SYNC (soup_request_get_session (req)))
+ g_main_loop_unref (data.loop);
+
+ return nread == 0;
}
gboolean
-soup_test_stream_close_async_as_sync (GInputStream *stream,
- GCancellable *cancellable,
- GError **error)
+soup_test_request_close_stream (SoupRequest *req,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
{
- AsyncAsSyncData data;
- gboolean ok;
+ AsyncAsSyncData data;
+ gboolean ok;
- data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
+ if (SOUP_IS_SESSION_SYNC (soup_request_get_session (req)))
+ return g_input_stream_close (stream, cancellable, error);
- g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, cancellable,
- async_as_sync_callback, &data);
- g_main_loop_run (data.loop);
+ data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
- ok = g_input_stream_close_finish (stream, data.result, error);
+ g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, cancellable,
+ async_as_sync_callback, &data);
+ g_main_loop_run (data.loop);
- g_main_loop_unref (data.loop);
- g_object_unref (data.result);
+ ok = g_input_stream_close_finish (stream, data.result, error);
- return ok;
+ g_main_loop_unref (data.loop);
+ g_object_unref (data.result);
+
+ return ok;
}
+
+void
+soup_test_register_resources (void)
+{
+ static gboolean registered = FALSE;
+ GResource *resource;
+ char *path;
+ GError *error = NULL;
+
+ if (registered)
+ return;
+
+ path = g_test_build_filename (G_TEST_BUILT, "soup-tests.gresource", NULL);
+ resource = g_resource_load (path, &error);
+ if (!resource) {
+ g_printerr ("Could not load resource soup-tests.gresource: %s\n",
+ error->message);
+ exit (1);
+ }
+ g_free (path);
+
+ g_resources_register (resource);
+ g_resource_unref (resource);
+
+ registered = TRUE;
+}
+
+SoupBuffer *
+soup_test_load_resource (const char *name,
+ GError **error)
+{
+ GBytes *bytes;
+ char *path;
+
+ soup_test_register_resources ();
+
+ path = g_build_path ("/", "/org/gnome/libsoup/tests/resources", name, NULL);
+ bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
+ g_free (path);
+
+ if (!bytes)
+ return NULL;
+
+ return soup_buffer_new_with_owner (g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ bytes,
+ (GDestroyNotify) g_bytes_unref);
+}
+
+SoupBuffer *
+soup_test_get_index (void)
+{
+ if (!index_buffer) {
+ char *path, *contents;
+ gsize length;
+ GError *error = NULL;
+
+ path = g_test_build_filename (G_TEST_DIST, "index.txt", NULL);
+ if (!g_file_get_contents (path, &contents, &length, &error)) {
+ g_printerr ("Could not read index.txt: %s\n",
+ error->message);
+ exit (1);
+ }
+ g_free (path);
+
+ index_buffer = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
+ }
+
+ return index_buffer;
+}
+
+#ifndef G_HAVE_ISO_VARARGS
+void
+soup_test_assert (gboolean expr, const char *fmt, ...)
+{
+ char *message;
+ va_list args;
+
+ if (G_UNLIKELY (!expr)) {
+ va_start (args, fmt);
+ message = g_strdup_vprintf (fmt, args);
+ va_end (args);
+
+ g_assertion_message (G_LOG_DOMAIN,
+ "???", 0, "???"
+ message);
+ g_free (message);
+ }
+}
+#endif
diff --git a/tests/test-utils.h b/tests/test-utils.h
index 9e969038..03637dcf 100644
--- a/tests/test-utils.h
+++ b/tests/test-utils.h
@@ -1,3 +1,5 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -9,22 +11,47 @@
#include "libsoup/soup.h"
#include "libsoup/soup-requester.h"
-#include "libsoup/soup-request-data.h"
-#include "libsoup/soup-request-file.h"
-#include "libsoup/soup-request-http.h"
void test_init (int argc, char **argv, GOptionEntry *entries);
void test_cleanup (void);
-extern int debug_level, errors;
-extern gboolean expect_warning, tls_available;
+extern int debug_level;
+extern gboolean tls_available;
+extern gboolean apache_available;
void debug_printf (int level, const char *format, ...) G_GNUC_PRINTF (2, 3);
+#define SOUP_TEST_SKIP_IF_NO_TLS \
+ G_STMT_START { \
+ if (!tls_available) { \
+ g_test_skip ("TLS is not available"); \
+ return; \
+ } \
+ } G_STMT_END
+
#ifdef HAVE_APACHE
void apache_init (void);
void apache_cleanup (void);
+#define SOUP_TEST_SKIP_IF_NO_APACHE
+#else
+#define apache_init()
+#define apache_cleanup()
+#define SOUP_TEST_SKIP_IF_NO_APACHE \
+ G_STMT_START { \
+ g_test_skip ("apache is not available"); \
+ return; \
+ } G_STMT_END
#endif
+typedef enum {
+ SOUP_TEST_REQUEST_NONE = 0,
+ SOUP_TEST_REQUEST_CANCEL_MESSAGE = (1 << 0),
+ SOUP_TEST_REQUEST_CANCEL_CANCELLABLE = (1 << 1),
+ SOUP_TEST_REQUEST_CANCEL_SOON = (1 << 2),
+ SOUP_TEST_REQUEST_CANCEL_IMMEDIATE = (1 << 3),
+ SOUP_TEST_REQUEST_CANCEL_PREEMPTIVE = (1 << 4),
+ SOUP_TEST_REQUEST_CANCEL_AFTER_SEND_FINISH = (1 << 5),
+} SoupTestRequestFlags;
+
SoupSession *soup_test_session_new (GType type, ...);
void soup_test_session_abort_unref (SoupSession *session);
@@ -32,9 +59,68 @@ 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);
-GInputStream *soup_test_request_send_async_as_sync (SoupRequest *req,
- GCancellable *cancellable,
- GError **error);
-gboolean soup_test_stream_close_async_as_sync (GInputStream *stream,
- GCancellable *cancellable,
- GError **error);
+GInputStream *soup_test_request_send (SoupRequest *req,
+ GCancellable *cancellable,
+ guint flags,
+ GError **error);
+gboolean soup_test_request_read_all (SoupRequest *req,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+gboolean soup_test_request_close_stream (SoupRequest *req,
+ GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+
+void soup_test_register_resources (void);
+SoupBuffer *soup_test_load_resource (const char *name,
+ GError **error);
+
+SoupBuffer *soup_test_get_index (void);
+
+#ifdef G_HAVE_ISO_VARARGS
+#define soup_test_assert(expr, ...) \
+G_STMT_START { \
+ char *_message; \
+ if (G_UNLIKELY (!(expr))) { \
+ _message = g_strdup_printf (__VA_ARGS__); \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, __LINE__, G_STRFUNC, \
+ _message); \
+ g_free (_message); \
+ } \
+} G_STMT_END
+#else
+void soup_test_assert (gboolean expr, const char *fmt, ...);
+#endif
+
+#define soup_test_assert_message_status(msg, status) \
+G_STMT_START { \
+ SoupMessage *_msg = (msg); \
+ guint _status = (status); \
+ char *_message; \
+ \
+ if (G_UNLIKELY (_msg->status_code != _status)) { \
+ _message = g_strdup_printf ("Unexpected status %d %s (expected %d %s)", \
+ _msg->status_code, _msg->reason_phrase, \
+ _status, soup_status_get_phrase (_status)); \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, __LINE__, G_STRFUNC, \
+ _message); \
+ g_free (_message); \
+ } \
+} G_STMT_END
+
+#define soup_assert_cmpmem(s1, l1, s2, l2) \
+G_STMT_START { \
+ int __l1 = l1, __l2 = l2; \
+ gconstpointer __s1 = s1, __s2 = s2; \
+ if (G_UNLIKELY ((__l1) != (__l2))) { \
+ g_assertion_message_cmpnum (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+ "len(" #s1 ") == len(" #s2 ")", __l1, "==", __l2, \
+ 'i'); \
+ } else if (G_UNLIKELY (memcmp (__s1, __s2, __l1) != 0)) { \
+ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+ "assertion failed (" #s1 " == " #s2 ")"); \
+ } \
+} G_STMT_END
diff --git a/tests/timeout-test.c b/tests/timeout-test.c
index 2bb91d8a..81fb4331 100644
--- a/tests/timeout-test.c
+++ b/tests/timeout-test.c
@@ -2,31 +2,14 @@
#include "test-utils.h"
+static gboolean slow_https;
+
static void
-do_message_to_session (SoupSession *session, const char *uri,
- const char *comment, guint expected_status)
+message_finished (SoupMessage *msg, gpointer user_data)
{
- SoupMessage *msg;
-
- debug_printf (1, " %s\n", comment);
- msg = soup_message_new ("GET", uri);
- soup_session_send_message (session, msg);
-
- if (msg->status_code != expected_status) {
- debug_printf (1, " FAILED: %d %s (expected %d %s)\n",
- msg->status_code, msg->reason_phrase,
- expected_status,
- soup_status_get_phrase (expected_status));
- errors++;
- }
+ gboolean *finished = user_data;
- if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code) &&
- !soup_message_is_keepalive (msg)) {
- debug_printf (1, " ERROR: message is not keepalive!");
- errors++;
- }
-
- g_object_unref (msg);
+ *finished = TRUE;
}
static void
@@ -39,10 +22,37 @@ request_started_cb (SoupSession *session, SoupMessage *msg,
}
static void
-do_tests_for_session (SoupSession *timeout_session,
- SoupSession *idle_session,
- SoupSession *plain_session,
- char *fast_uri, char *slow_uri)
+do_message_to_session (SoupSession *session, const char *uri,
+ const char *comment, guint expected_status)
+{
+ SoupMessage *msg;
+ gboolean finished = FALSE;
+
+ if (comment)
+ debug_printf (1, " msg %s\n", comment);
+ msg = soup_message_new ("GET", uri);
+
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (message_finished), &finished);
+ soup_session_send_message (session, msg);
+
+ soup_test_assert_message_status (msg, expected_status);
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ g_assert_true (soup_message_is_keepalive (msg));
+ g_assert_true (finished);
+
+ g_signal_handlers_disconnect_by_func (msg,
+ G_CALLBACK (message_finished),
+ &finished);
+ g_object_unref (msg);
+}
+
+static void
+do_msg_tests_for_session (SoupSession *timeout_session,
+ SoupSession *idle_session,
+ SoupSession *plain_session,
+ const char *fast_uri,
+ const char *slow_uri)
{
SoupSocket *ret, *idle_first, *idle_second;
SoupSocket *plain_first, *plain_second;
@@ -51,14 +61,14 @@ do_tests_for_session (SoupSession *timeout_session,
g_signal_connect (idle_session, "request-started",
G_CALLBACK (request_started_cb), &ret);
do_message_to_session (idle_session, fast_uri, "fast to idle", SOUP_STATUS_OK);
- idle_first = ret;
+ idle_first = g_object_ref (ret);
}
if (plain_session) {
g_signal_connect (plain_session, "request-started",
G_CALLBACK (request_started_cb), &ret);
do_message_to_session (plain_session, fast_uri, "fast to plain", SOUP_STATUS_OK);
- plain_first = ret;
+ plain_first = g_object_ref (ret);
}
do_message_to_session (timeout_session, fast_uri, "fast to timeout", SOUP_STATUS_OK);
@@ -71,10 +81,9 @@ do_tests_for_session (SoupSession *timeout_session,
(gpointer)request_started_cb,
&ret);
- if (idle_first == idle_second) {
- debug_printf (1, " ERROR: idle_session did not close first connection\n");
- errors++;
- }
+ soup_test_assert (idle_first != idle_second,
+ "idle_session did not close first connection");
+ g_object_unref (idle_first);
}
if (plain_session) {
@@ -84,46 +93,174 @@ do_tests_for_session (SoupSession *timeout_session,
(gpointer)request_started_cb,
&ret);
- if (plain_first != plain_second) {
- debug_printf (1, " ERROR: plain_session closed connection\n");
- errors++;
- }
+ soup_test_assert (plain_first == plain_second,
+ "plain_session closed connection");
+ g_object_unref (plain_first);
+ }
+}
+
+static void
+do_request_to_session (SoupSession *session, const char *uri,
+ const char *comment, gboolean expect_timeout)
+{
+ SoupRequest *req;
+ SoupMessage *msg;
+ GInputStream *stream;
+ GError *error = NULL;
+ gboolean finished = FALSE;
+
+ debug_printf (1, " req %s\n", comment);
+ req = soup_session_request (session, uri, NULL);
+ msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
+
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (message_finished), &finished);
+ stream = soup_test_request_send (req, NULL, 0, &error);
+
+ if (expect_timeout)
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
+ else
+ g_assert_no_error (error);
+ g_clear_error (&error);
+
+ if (stream) {
+ soup_test_request_read_all (req, stream, NULL, &error);
+ g_assert_no_error (error);
+ }
+
+ if (stream) {
+ soup_test_request_close_stream (req, stream, NULL, &error);
+ g_assert_no_error (error);
+ g_object_unref (stream);
}
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ g_assert_true (soup_message_is_keepalive (msg));
+ g_assert_true (finished);
+
+ g_signal_handlers_disconnect_by_func (msg,
+ G_CALLBACK (message_finished),
+ &finished);
+ g_object_unref (msg);
+ g_object_unref (req);
}
static void
-do_timeout_tests (char *fast_uri, char *slow_uri)
+do_req_tests_for_session (SoupSession *timeout_session,
+ SoupSession *idle_session,
+ SoupSession *plain_session,
+ const char *fast_uri,
+ const char *slow_uri)
+{
+ SoupSocket *ret, *idle_first, *idle_second;
+ SoupSocket *plain_first, *plain_second;
+
+ if (idle_session) {
+ g_signal_connect (idle_session, "request-started",
+ G_CALLBACK (request_started_cb), &ret);
+ do_request_to_session (idle_session, fast_uri, "fast to idle", FALSE);
+ idle_first = g_object_ref (ret);
+ }
+
+ if (plain_session) {
+ g_signal_connect (plain_session, "request-started",
+ G_CALLBACK (request_started_cb), &ret);
+ do_request_to_session (plain_session, fast_uri, "fast to plain", FALSE);
+ plain_first = g_object_ref (ret);
+ }
+
+ do_request_to_session (timeout_session, fast_uri, "fast to timeout", FALSE);
+ do_request_to_session (timeout_session, slow_uri, "slow to timeout", TRUE);
+
+ if (idle_session) {
+ do_request_to_session (idle_session, fast_uri, "fast to idle", FALSE);
+ idle_second = ret;
+ g_signal_handlers_disconnect_by_func (idle_session,
+ (gpointer)request_started_cb,
+ &ret);
+
+ soup_test_assert (idle_first != idle_second,
+ "idle_session did not close first connection");
+ g_object_unref (idle_first);
+ }
+
+ if (plain_session) {
+ do_request_to_session (plain_session, fast_uri, "fast to plain", FALSE);
+ plain_second = ret;
+ g_signal_handlers_disconnect_by_func (plain_session,
+ (gpointer)request_started_cb,
+ &ret);
+
+ soup_test_assert (plain_first == plain_second,
+ "plain_session closed connection");
+ g_object_unref (plain_first);
+ }
+}
+
+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);
+ gboolean extra_slow;
+
+ if (g_str_has_prefix (fast_uri, "https")) {
+ SOUP_TEST_SKIP_IF_NO_TLS;
+
+ extra_slow = slow_https;
+ } else
+ extra_slow = FALSE;
- debug_printf (1, " async\n");
timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_TIMEOUT, 1,
- NULL);
+ SOUP_SESSION_TIMEOUT, extra_slow ? 3 : 1,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
idle_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_IDLE_TIMEOUT, 1,
+ SOUP_SESSION_IDLE_TIMEOUT, extra_slow ? 2 : 1,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
/* The "plain" session also has an idle timeout, but it's longer
* than the test takes, so for our purposes it should behave like
* it has no timeout.
*/
plain_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
- SOUP_SESSION_IDLE_TIMEOUT, 2,
+ SOUP_SESSION_IDLE_TIMEOUT, 20,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
NULL);
- do_tests_for_session (timeout_session, idle_session, plain_session,
- fast_uri, slow_uri);
+
+ do_msg_tests_for_session (timeout_session, idle_session, plain_session,
+ fast_uri, slow_uri);
+ do_req_tests_for_session (timeout_session, idle_session, plain_session,
+ fast_uri, slow_uri);
soup_test_session_abort_unref (timeout_session);
soup_test_session_abort_unref (idle_session);
soup_test_session_abort_unref (plain_session);
+}
+
+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);
+ gboolean extra_slow;
+
+ if (g_str_has_prefix (fast_uri, "https")) {
+ SOUP_TEST_SKIP_IF_NO_TLS;
+
+ extra_slow = slow_https;
+ } else
+ extra_slow = FALSE;
- debug_printf (1, " sync\n");
timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
- SOUP_SESSION_TIMEOUT, 1,
+ SOUP_SESSION_TIMEOUT, extra_slow ? 3 : 1,
NULL);
/* SOUP_SESSION_TIMEOUT doesn't work with sync sessions */
plain_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
NULL);
- do_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri);
+ do_msg_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri);
+ 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);
}
@@ -154,44 +291,63 @@ server_handler (SoupServer *server,
soup_server_pause_message (server, msg);
g_object_set_data (G_OBJECT (msg), "server", server);
soup_add_timeout (soup_server_get_async_context (server),
- 1100, timeout_finish_message, msg);
+ 4000, timeout_finish_message, msg);
}
}
int
main (int argc, char **argv)
{
- SoupServer *server;
- char *fast_uri, *slow_uri;
+ SoupServer *server, *https_server = NULL;
+ char *uri, *https_uri = NULL;
+ int ret;
test_init (argc, argv, NULL);
- debug_printf (1, "http\n");
server = soup_test_server_new (TRUE);
soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
- fast_uri = g_strdup_printf ("http://127.0.0.1:%u/",
- soup_server_get_port (server));
- slow_uri = g_strdup_printf ("http://127.0.0.1:%u/slow",
- soup_server_get_port (server));
- do_timeout_tests (fast_uri, slow_uri);
- g_free (fast_uri);
- g_free (slow_uri);
- soup_test_server_quit_unref (server);
+ uri = g_strdup_printf ("http://127.0.0.1:%u/",
+ soup_server_get_port (server));
if (tls_available) {
- debug_printf (1, "\nhttps\n");
- server = soup_test_server_new_ssl (TRUE);
- soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
- fast_uri = g_strdup_printf ("https://127.0.0.1:%u/",
- soup_server_get_port (server));
- slow_uri = g_strdup_printf ("https://127.0.0.1:%u/slow",
- soup_server_get_port (server));
- do_timeout_tests (fast_uri, slow_uri);
- g_free (fast_uri);
- g_free (slow_uri);
- soup_test_server_quit_unref (server);
- }
+ SoupSession *test_session;
+ gint64 start, end;
+
+ https_server = soup_test_server_new_ssl (TRUE);
+ 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));
+
+ /* The 1-second timeouts are too fast for some machines... */
+ test_session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
+ start = g_get_monotonic_time ();
+ do_message_to_session (test_session, uri, NULL, SOUP_STATUS_OK);
+ end = g_get_monotonic_time ();
+ soup_test_session_abort_unref (test_session);
+ debug_printf (2, " (https request took %0.3fs)\n", (end - start) / 1000000.0);
+ if (end - start > 750000) {
+ debug_printf (1, " (using extra-slow mode)\n\n");
+ slow_https = TRUE;
+ } else {
+ debug_printf (2, "\n");
+ slow_https = FALSE;
+ }
+ } else
+ https_uri = g_strdup ("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);
+ g_test_add_data_func ("/timeout/https/async", https_uri, do_async_timeout_tests);
+ g_test_add_data_func ("/timeout/https/sync", https_uri, do_sync_timeout_tests);
+
+ ret = g_test_run ();
+
+ g_free (uri);
+ g_free (https_uri);
+ soup_test_server_quit_unref (server);
+ if (https_server)
+ soup_test_server_quit_unref (https_server);
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/tld-test.c b/tests/tld-test.c
index 5f66c686..4fad8625 100644
--- a/tests/tld-test.c
+++ b/tests/tld-test.c
@@ -7,124 +7,186 @@
/* From http://publicsuffix.org/list/test.txt */
static struct {
- const char *hostname;
- const char *result;
+ const char *hostname;
+ const char *result;
+ SoupTLDError error;
} tld_tests[] = {
- /* NULL input. Not checked here because the API requires a valid hostname. */
- /* { NULL, NULL }, */
- /* Mixed case. Not checked because the API requires a valid hostname. */
- /* { "COM", NULL }, */
- /* { "example.COM", "example.com" }, */
- /* { "WwW.example.COM", "example.com" }, */
- /* Leading dot. */
- { ".com", NULL },
- { ".example", NULL },
- { ".example.com", NULL },
- { ".example.example", NULL },
- /* Unlisted TLD.*/
- { "example", NULL },
- { "example.example", NULL },
- { "b.example.example", NULL },
- { "a.b.example.example", NULL },
- /* Listed, but non-Internet, TLD. */
- { "local", NULL },
- { "example.local", NULL },
- { "b.example.local", NULL },
- { "a.b.example.local", NULL },
- /* TLD with only 1 rule. */
- { "biz", NULL },
- { "domain.biz", "domain.biz" },
- { "b.domain.biz", "domain.biz" },
- { "a.b.domain.biz", "domain.biz" },
- /* TLD with some 2-level rules. */
- { "com", NULL },
- { "example.com", "example.com" },
- { "b.example.com", "example.com" },
- { "a.b.example.com", "example.com" },
- { "uk.com", NULL },
- { "example.uk.com", "example.uk.com" },
- { "b.example.uk.com", "example.uk.com" },
- { "a.b.example.uk.com", "example.uk.com" },
- { "test.ac", "test.ac" },
- /* TLD with only 1 (wildcard) rule. */
- { "cy", NULL },
- { "c.cy", NULL },
- { "b.c.cy", "b.c.cy" },
- { "a.b.c.cy", "b.c.cy" },
- /* More complex TLD. */
- { "jp", NULL },
- { "test.jp", "test.jp" },
- { "www.test.jp", "test.jp" },
- { "ac.jp", NULL },
- { "test.ac.jp", "test.ac.jp" },
- { "www.test.ac.jp", "test.ac.jp" },
- { "kyoto.jp", NULL },
- { "c.kyoto.jp", NULL },
- { "b.c.kyoto.jp", "b.c.kyoto.jp" },
- { "a.b.c.kyoto.jp", "b.c.kyoto.jp" },
- { "pref.kyoto.jp", "pref.kyoto.jp" }, /* Exception rule. */
- { "www.pref.kyoto.jp", "pref.kyoto.jp" }, /* Exception rule. */
- { "city.kyoto.jp", "city.kyoto.jp" }, /* Exception rule. */
- { "www.city.kyoto.jp", "city.kyoto.jp" }, /* Exception rule. */
- /* TLD with a wildcard rule and exceptions. */
- { "om", NULL },
- { "test.om", NULL },
- { "b.test.om", "b.test.om" },
- { "a.b.test.om", "b.test.om" },
- { "songfest.om", "songfest.om" },
- { "www.songfest.om", "songfest.om" },
- /* US K12. */
- { "us", NULL },
- { "test.us", "test.us" },
- { "www.test.us", "test.us" },
- { "ak.us", NULL },
- { "test.ak.us", "test.ak.us" },
- { "www.test.ak.us", "test.ak.us" },
- { "k12.ak.us", NULL },
- { "test.k12.ak.us", "test.k12.ak.us" },
- { "www.test.k12.ak.us", "test.k12.ak.us" },
- /* This is not in http://publicsuffix.org/list/test.txt but we want to check it anyway. */
- { "co.uk", NULL },
- /* The original list does not include non-ASCII tests. Let's add a couple. */
- { "公司.cn", NULL },
- { "a.b.åfjord.no", "b.åfjord.no" }
+ /* NULL input. Not checked here because the API requires a valid hostname. */
+ /* { NULL, NULL, -1 }, */
+ /* Mixed case. Not checked because the API requires a valid hostname. */
+ /* { "COM", NULL, -1 }, */
+ /* { "example.COM", "example.com", -1 }, */
+ /* { "WwW.example.COM", "example.com", -1 }, */
+ /* Leading dot. */
+ { ".com", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME },
+ { ".example", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME },
+ { ".example.com", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME },
+ { ".example.example", NULL, SOUP_TLD_ERROR_INVALID_HOSTNAME },
+ /* TLD with only 1 rule. */
+ { "biz", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "domain.biz", "domain.biz", -1 },
+ { "b.domain.biz", "domain.biz", -1 },
+ { "a.b.domain.biz", "domain.biz", -1 },
+ /* TLD with some 2-level rules. */
+ { "com", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "example.com", "example.com", -1 },
+ { "b.example.com", "example.com", -1 },
+ { "a.b.example.com", "example.com", -1 },
+ { "uk.com", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "example.uk.com", "example.uk.com", -1 },
+ { "b.example.uk.com", "example.uk.com", -1 },
+ { "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 },
+ /* More complex TLD. */
+ { "jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.jp", "test.jp", -1 },
+ { "www.test.jp", "test.jp", -1 },
+ { "ac.jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.ac.jp", "test.ac.jp", -1 },
+ { "www.test.ac.jp", "test.ac.jp", -1 },
+ { "kyoto.jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "minami.kyoto.jp", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "b.minami.kyoto.jp", "b.minami.kyoto.jp", -1 },
+ { "a.b.minami.kyoto.jp", "b.minami.kyoto.jp", -1 },
+ { "pref.kyoto.jp", "pref.kyoto.jp", -1 },
+ { "www.pref.kyoto.jp", "pref.kyoto.jp", -1 },
+ { "city.kyoto.jp", "city.kyoto.jp", -1 },
+ { "www.city.kyoto.jp", "city.kyoto.jp", -1 },
+ /* TLD with a wildcard rule and exceptions. */
+ { "ck", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.ck", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "b.test.ck", "b.test.ck", -1 },
+ { "a.b.test.ck", "b.test.ck", -1 },
+ { "www.ck", "www.ck", -1 },
+ { "www.www.ck", "www.ck", -1 },
+ /* US K12. */
+ { "us", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.us", "test.us", -1 },
+ { "www.test.us", "test.us", -1 },
+ { "ak.us", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.ak.us", "test.ak.us", -1 },
+ { "www.test.ak.us", "test.ak.us", -1 },
+ { "k12.ak.us", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.k12.ak.us", "test.k12.ak.us", -1 },
+ { "www.test.k12.ak.us", "test.k12.ak.us", -1 },
+ /* IDN labels. */
+ { "食狮.com.cn", "食狮.com.cn", -1 },
+ { "食狮.公司.cn", "食狮.公司.cn", -1 },
+ { "www.食狮.公司.cn", "食狮.公司.cn", -1 },
+ { "shishi.公司.cn", "shishi.公司.cn", -1 },
+ { "公司.cn", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "食狮.中国", "食狮.中国", -1 },
+ { "www.食狮.中国", "食狮.中国", -1 },
+ { "shishi.中国", "shishi.中国", -1 },
+ { "中国", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ /* Same as above, but punycoded. */
+ { "xn--85x722f.com.cn", "xn--85x722f.com.cn", -1 },
+ { "xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn", -1 },
+ { "www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn", -1 },
+ { "shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn", -1 },
+ { "xn--55qx5d.cn", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s", -1 },
+ { "www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s", -1 },
+ { "shishi.xn--fiqs8s", "shishi.xn--fiqs8s", -1 },
+ { "xn--fiqs8s", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ /* End of publicsuffix.org tests */
+
+ /* Let's just double-check this one... */
+ { "co.uk", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.co.uk", "test.co.uk", -1 },
+ { "www.test.co.uk", "test.co.uk", -1 },
+
+ /* Two levels of non-ASCII */
+ { "våler.østfold.no", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.våler.østfold.no", "test.våler.østfold.no", -1 },
+ { "www.test.våler.østfold.no", "test.våler.østfold.no", -1 },
+ { "xn--vler-qoa.xn--stfold-9xa.no", NULL, SOUP_TLD_ERROR_NOT_ENOUGH_DOMAINS },
+ { "test.xn--vler-qoa.xn--stfold-9xa.no", "test.xn--vler-qoa.xn--stfold-9xa.no", -1 },
+ { "www.test.xn--vler-qoa.xn--stfold-9xa.no", "test.xn--vler-qoa.xn--stfold-9xa.no", -1 },
+},
+
+/* Non Internet TLDs have NULL as expected result
+ */
+non_inet_tld_tests[] = {
+ /* Unlisted TLD.*/
+ { "example", NULL },
+ { "example.example", NULL },
+ { "b.example.example", NULL },
+ { "a.b.example.example", NULL },
+ /* Listed, but non-Internet, TLD. */
+ { "local", NULL },
+ { "example.local", NULL },
+ { "b.example.local", NULL },
+ { "a.b.example.local", NULL }
};
+static void
+do_inet_tests (void)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (tld_tests); i++) {
+ GError *error = NULL;
+ gboolean is_public;
+ const char *base_domain;
+
+ debug_printf (1, "Testing %s\n", tld_tests[i].hostname);
+
+ is_public = soup_tld_domain_is_public_suffix (tld_tests[i].hostname);
+ base_domain = soup_tld_get_base_domain (tld_tests[i].hostname, &error);
+
+ if (base_domain) {
+ g_assert_no_error (error);
+ g_assert_false (is_public);
+ g_assert_cmpstr (base_domain, ==, tld_tests[i].result);
+ } else {
+ g_assert_null (tld_tests[i].result);
+ g_assert_error (error, SOUP_TLD_ERROR, tld_tests[i].error);
+ g_clear_error (&error);
+ }
+ }
+}
+
+static void
+do_non_inet_tests (void)
+{
+ int i;
+
+ g_test_bug ("679230");
+ g_test_bug ("681085");
+
+ for (i = 0; i < G_N_ELEMENTS (non_inet_tld_tests); i++) {
+ gboolean is_public;
+ const char *base_domain;
+
+ debug_printf (1, "Testing %s\n", non_inet_tld_tests[i].hostname);
+
+ is_public = soup_tld_domain_is_public_suffix (non_inet_tld_tests[i].hostname);
+ base_domain = soup_tld_get_base_domain (non_inet_tld_tests[i].hostname, NULL);
+
+ g_assert_false (is_public);
+ g_assert_null (base_domain);
+ }
+}
+
int
main (int argc, char **argv)
{
- int i;
+ int ret;
test_init (argc, argv, NULL);
- errors = 0;
- for (i = 0; i < G_N_ELEMENTS (tld_tests); ++i) {
- gboolean is_public = soup_tld_domain_is_public_suffix (tld_tests[i].hostname);
- const char *base_domain = soup_tld_get_base_domain (tld_tests[i].hostname, NULL);
-
- debug_printf (1, "Testing %s: ", tld_tests[i].hostname);
- if (tld_tests[i].result) {
- /* Public domains have NULL expected results. */
- if (is_public || g_strcmp0 (tld_tests[i].result, base_domain)) {
- debug_printf (1, "ERROR: %s got %s (%s expected)\n",
- tld_tests[i].hostname, base_domain, tld_tests[i].result);
- ++errors;
- } else
- debug_printf (1, "OK\n");
- } else {
- /* If there is no expected result then either the domain is public or
- * the hostname invalid (for example starts with a leading dot).
- */
- if (!is_public && base_domain) {
- debug_printf (1, "ERROR: public domain %s got %s (none expected)\n",
- tld_tests[i].hostname, base_domain);
- ++errors;
- } else
- debug_printf (1, "OK\n");
- }
- }
+ g_test_add_func ("/tld/inet", do_inet_tests);
+ g_test_add_func ("/tld/non-inet", do_non_inet_tests);
+
+ ret = g_test_run ();
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/uri-parsing.c b/tests/uri-parsing.c
index 08274a28..d56b655f 100644
--- a/tests/uri-parsing.c
+++ b/tests/uri-parsing.c
@@ -3,123 +3,155 @@
#include "test-utils.h"
static struct {
- const char *uri_string, *result;
+ const char *uri_string, *result, *bugref;
const SoupURI bits;
} abs_tests[] = {
- { "foo:", "foo:",
+ { "foo:", "foo:", NULL,
{ "foo", NULL, NULL, NULL, 0, "", NULL, NULL } },
- { "file:/dev/null", "file:/dev/null",
+ { "file:/dev/null", "file:/dev/null", NULL,
{ "file", NULL, NULL, NULL, 0, "/dev/null", NULL, NULL } },
- { "file:///dev/null", "file:///dev/null",
+ { "file:///dev/null", "file:///dev/null", NULL,
{ "file", NULL, NULL, "", 0, "/dev/null", NULL, NULL } },
- { "ftp://user@host/path", "ftp://user@host/path",
+ { "ftp://user@host/path", "ftp://user@host/path", NULL,
{ "ftp", "user", NULL, "host", 21, "/path", NULL, NULL } },
- { "ftp://user@host:9999/path", "ftp://user@host:9999/path",
+ { "ftp://user@host:9999/path", "ftp://user@host:9999/path", NULL,
{ "ftp", "user", NULL, "host", 9999, "/path", NULL, NULL } },
- { "ftp://user:password@host/path", "ftp://user@host/path",
+ { "ftp://user:password@host/path", "ftp://user@host/path", NULL,
{ "ftp", "user", "password", "host", 21, "/path", NULL, NULL } },
- { "ftp://user:password@host:9999/path", "ftp://user@host:9999/path",
+ { "ftp://user:password@host:9999/path", "ftp://user@host:9999/path", NULL,
{ "ftp", "user", "password", "host", 9999, "/path", NULL, NULL } },
- { "ftp://user:password@host", "ftp://user@host",
+ { "ftp://user:password@host", "ftp://user@host", NULL,
{ "ftp", "user", "password", "host", 21, "", NULL, NULL } },
- { "http://us%65r@host", "http://user@host/",
+ { "http://us%65r@host", "http://user@host/", NULL,
{ "http", "user", NULL, "host", 80, "/", NULL, NULL } },
- { "http://us%40r@host", "http://us%40r@host/",
+ { "http://us%40r@host", "http://us%40r@host/", NULL,
{ "http", "us\x40r", NULL, "host", 80, "/", NULL, NULL } },
- { "http://us%3ar@host", "http://us%3Ar@host/",
+ { "http://us%3ar@host", "http://us%3Ar@host/", NULL,
{ "http", "us\x3ar", NULL, "host", 80, "/", NULL, NULL } },
- { "http://us%2fr@host", "http://us%2Fr@host/",
+ { "http://us%2fr@host", "http://us%2Fr@host/", NULL,
{ "http", "us\x2fr", NULL, "host", 80, "/", NULL, NULL } },
- { "http://us%3fr@host", "http://us%3Fr@host/",
+ { "http://us%3fr@host", "http://us%3Fr@host/", NULL,
{ "http", "us\x3fr", NULL, "host", 80, "/", NULL, NULL } },
- { "http://host?query", "http://host/?query",
+ { "http://host?query", "http://host/?query", NULL,
{ "http", NULL, NULL, "host", 80, "/", "query", NULL } },
{ "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value",
- "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value",
+ "http://host/path?query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value", NULL,
{ "http", NULL, NULL, "host", 80, "/path", "query=http%3A%2F%2Fhost%2Fpath%3Fchildparam%3Dchildvalue&param=value", NULL } },
{ "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F",
- "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F",
+ "http://control-chars/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", NULL,
{ "http", NULL, NULL, "control-chars", 80, "/%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F", NULL, NULL } },
{ "http://space/%20",
- "http://space/%20",
+ "http://space/%20", NULL,
{ "http", NULL, NULL, "space", 80, "/%20", NULL, NULL } },
{ "http://delims/%3C%3E%23%25%22",
- "http://delims/%3C%3E%23%25%22",
+ "http://delims/%3C%3E%23%25%22", NULL,
{ "http", NULL, NULL, "delims", 80, "/%3C%3E%23%25%22", NULL, NULL } },
{ "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60",
- "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60",
+ "http://unwise-chars/%7B%7D%7C%5C%5E%5B%5D%60", NULL,
{ "http", NULL, NULL, "unwise-chars", 80, "/%7B%7D%7C%5C%5E%5B%5D%60", NULL, NULL } },
/* From RFC 2732 */
{ "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html",
- "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html",
+ "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/index.html", NULL,
{ "http", NULL, NULL, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", 80, "/index.html", NULL, NULL } },
{ "http://[1080:0:0:0:8:800:200C:417A]/index.html",
- "http://[1080:0:0:0:8:800:200C:417A]/index.html",
+ "http://[1080:0:0:0:8:800:200C:417A]/index.html", NULL,
{ "http", NULL, NULL, "1080:0:0:0:8:800:200C:417A", 80, "/index.html", NULL, NULL } },
{ "http://[3ffe:2a00:100:7031::1]",
- "http://[3ffe:2a00:100:7031::1]/",
+ "http://[3ffe:2a00:100:7031::1]/", NULL,
{ "http", NULL, NULL, "3ffe:2a00:100:7031::1", 80, "/", NULL, NULL } },
{ "http://[1080::8:800:200C:417A]/foo",
- "http://[1080::8:800:200C:417A]/foo",
+ "http://[1080::8:800:200C:417A]/foo", NULL,
{ "http", NULL, NULL, "1080::8:800:200C:417A", 80, "/foo", NULL, NULL } },
{ "http://[::192.9.5.5]/ipng",
- "http://[::192.9.5.5]/ipng",
+ "http://[::192.9.5.5]/ipng", NULL,
{ "http", NULL, NULL, "::192.9.5.5", 80, "/ipng", NULL, NULL } },
{ "http://[::FFFF:129.144.52.38]:80/index.html",
- "http://[::FFFF:129.144.52.38]/index.html",
+ "http://[::FFFF:129.144.52.38]/index.html", NULL,
{ "http", NULL, NULL, "::FFFF:129.144.52.38", 80, "/index.html", NULL, NULL } },
{ "http://[2010:836B:4179::836B:4179]",
- "http://[2010:836B:4179::836B:4179]/",
+ "http://[2010:836B:4179::836B:4179]/", NULL,
{ "http", NULL, NULL, "2010:836B:4179::836B:4179", 80, "/", NULL, NULL } },
/* Try to recover certain kinds of invalid URIs */
{ "http://host/path with spaces",
- "http://host/path%20with%20spaces",
+ "http://host/path%20with%20spaces", "566530",
{ "http", NULL, NULL, "host", 80, "/path%20with%20spaces", NULL, NULL } },
- { " http://host/path", "http://host/path",
+ { " http://host/path", "http://host/path", "594405",
{ "http", NULL, NULL, "host", 80, "/path", NULL, NULL } },
- { "http://host/path ", "http://host/path",
+ { "http://host/path ", "http://host/path", "594405",
{ "http", NULL, NULL, "host", 80, "/path", NULL, NULL } },
- { "http://host ", "http://host/",
+ { "http://host ", "http://host/", "594405",
{ "http", NULL, NULL, "host", 80, "/", NULL, NULL } },
- { "http://host:999 ", "http://host:999/",
+ { "http://host:999 ", "http://host:999/", "594405",
{ "http", NULL, NULL, "host", 999, "/", NULL, NULL } },
- { "http://host/pa\nth", "http://host/path",
+ { "http://host/pa\nth", "http://host/path", "594405",
{ "http", NULL, NULL, "host", 80, "/path", NULL, NULL } },
- { "http:\r\n//host/path", "http://host/path",
+ { "http:\r\n//host/path", "http://host/path", "594405",
{ "http", NULL, NULL, "host", 80, "/path", NULL, NULL } },
- { "http://\thost/path", "http://host/path",
+ { "http://\thost/path", "http://host/path", "594405",
{ "http", NULL, NULL, "host", 80, "/path", NULL, NULL } },
- /* Bug 594405; 0-length is different from not-present */
- { "http://host/path?", "http://host/path?",
+ /* 0-length is different from not-present */
+ { "http://host/path?", "http://host/path?", "594405",
{ "http", NULL, NULL, "host", 80, "/path", "", NULL } },
- { "http://host/path#", "http://host/path#",
+ { "http://host/path#", "http://host/path#", "594405",
{ "http", NULL, NULL, "host", 80, "/path", NULL, "" } },
- /* Bug 590524; ignore badly-%-encoding */
- { "http://host/path%", "http://host/path%",
+ /* ignore bad %-encoding */
+ { "http://host/path%", "http://host/path%", "590524",
{ "http", NULL, NULL, "host", 80, "/path%", NULL, NULL } },
- { "http://h%ost/path", "http://h%25ost/path",
+ { "http://h%ost/path", "http://h%25ost/path", "590524",
{ "http", NULL, NULL, "h%ost", 80, "/path", NULL, NULL } },
- { "http://host/path%%", "http://host/path%%",
+ { "http://host/path%%", "http://host/path%%", "590524",
{ "http", NULL, NULL, "host", 80, "/path%%", NULL, NULL } },
- { "http://host/path%%%", "http://host/path%%%",
+ { "http://host/path%%%", "http://host/path%%%", "590524",
{ "http", NULL, NULL, "host", 80, "/path%%%", NULL, NULL } },
- { "http://host/path%/x/", "http://host/path%/x/",
+ { "http://host/path%/x/", "http://host/path%/x/", "590524",
{ "http", NULL, NULL, "host", 80, "/path%/x/", NULL, NULL } },
- { "http://host/path%0x/", "http://host/path%0x/",
+ { "http://host/path%0x/", "http://host/path%0x/", "590524",
{ "http", NULL, NULL, "host", 80, "/path%0x/", NULL, NULL } },
- { "http://host/path%ax", "http://host/path%ax",
+ { "http://host/path%ax", "http://host/path%ax", "590524",
{ "http", NULL, NULL, "host", 80, "/path%ax", NULL, NULL } },
- /* Bug 662806; %-encode non-ASCII characters */
- { "http://host/p\xc3\xa4th/", "http://host/p%C3%A4th/",
+ /* %-encode non-ASCII characters */
+ { "http://host/p\xc3\xa4th/", "http://host/p%C3%A4th/", "662806",
{ "http", NULL, NULL, "host", 80, "/p%C3%A4th/", NULL, NULL } },
- { "HTTP:////////////////", "http:////////////////",
+ { "HTTP:////////////////", "http:////////////////", "667637",
{ "http", NULL, NULL, "", 80, "//////////////", NULL, NULL } },
+
+ { "http://@host", "http://@host/", NULL,
+ { "http", "", NULL, "host", 80, "/", NULL, NULL } },
+ { "http://:@host", "http://@host/", NULL,
+ { "http", "", "", "host", 80, "/", NULL, NULL } },
+
+ { "http://host/keep%00nuls", "http://host/keep%00nuls", NULL,
+ { "http", NULL, NULL, "host", 80, "/keep%00nuls", NULL, NULL } },
+
+ /* scheme parsing */
+ { "foo0://host/path", "foo0://host/path", "703776",
+ { "foo0", NULL, NULL, "host", 0, "/path", NULL, NULL } },
+ { "f0.o://host/path", "f0.o://host/path", "703776",
+ { "f0.o", NULL, NULL, "host", 0, "/path", NULL, NULL } },
+ { "http++://host/path", "http++://host/path", "703776",
+ { "http++", NULL, NULL, "host", 0, "/path", NULL, NULL } },
+ { "http-ish://host/path", "http-ish://host/path", "703776",
+ { "http-ish", NULL, NULL, "host", 0, "/path", NULL, NULL } },
+ { "99http://host/path", NULL, "703776",
+ { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } },
+ { ".http://host/path", NULL, "703776",
+ { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } },
+ { "+http://host/path", NULL, "703776",
+ { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } },
+
+ /* IPv6 scope ID parsing (both correct and incorrect) */
+ { "http://[fe80::dead:beef%em1]/", "http://[fe80::dead:beef%25em1]/", NULL,
+ { "http", NULL, NULL, "fe80::dead:beef%em1", 80, "/", NULL, NULL } },
+ { "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 } }
};
static int num_abs_tests = G_N_ELEMENTS(abs_tests);
@@ -223,50 +255,18 @@ static struct {
static int num_rel_tests = G_N_ELEMENTS(rel_tests);
static struct {
- const char *one, *two;
+ const char *one, *two, *bugref;
} eq_tests[] = {
- { "example://a/b/c/%7Bfoo%7D", "eXAMPLE://a/./b/../b/%63/%7Bfoo%7D" },
- { "http://example.com", "http://example.com/" },
+ { "example://a/b/c/%7Bfoo%7D", "eXAMPLE://a/./b/../b/%63/%7Bfoo%7D", "628728" },
+ { "http://example.com", "http://example.com/", NULL },
/* From RFC 2616 */
- { "http://abc.com:80/~smith/home.html", "http://abc.com:80/~smith/home.html" },
- { "http://abc.com:80/~smith/home.html", "http://ABC.com/%7Esmith/home.html" },
- { "http://abc.com:80/~smith/home.html", "http://ABC.com:/%7esmith/home.html" },
+ { "http://abc.com:80/~smith/home.html", "http://abc.com:80/~smith/home.html", NULL },
+ { "http://abc.com:80/~smith/home.html", "http://ABC.com/%7Esmith/home.html", NULL },
+ { "http://abc.com:80/~smith/home.html", "http://ABC.com:/%7esmith/home.html", NULL },
};
static int num_eq_tests = G_N_ELEMENTS(eq_tests);
-#define test_cmpstr(a, b) _test_cmpstr (#a, #b, a, b)
-
-static gboolean
-_test_cmpstr (const char *got_desc,
- const char *exp_desc,
- const char *got,
- const char *expected)
-{
- if (got == expected)
- return TRUE;
-
- if (got == NULL) {
- debug_printf (1, "ERR\n %s = NULL, expected %s = \"%s\"\n",
- got_desc, exp_desc, expected);
- return FALSE;
- }
-
- if (expected == NULL) {
- debug_printf (1, "ERR\n %s = \"%s\", expected %s = NULL\n",
- got_desc, got, exp_desc);
- return FALSE;
- }
-
- if (strcmp (got, expected) != 0) {
- debug_printf (1, "ERR\n %s = \"%s\", expected %s = \"%s\"\n",
- got_desc, got, exp_desc, expected);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
+static void
do_uri (SoupURI *base_uri, const char *base_str,
const char *in_uri, const char *out_uri,
const SoupURI *bits)
@@ -275,76 +275,94 @@ do_uri (SoupURI *base_uri, const char *base_str,
char *uri_string;
if (base_uri) {
- debug_printf (1, "<%s> + <%s> = <%s>? ", base_str, in_uri,
+ debug_printf (1, "<%s> + <%s> = <%s>\n", base_str, in_uri,
out_uri ? out_uri : "ERR");
uri = soup_uri_new_with_base (base_uri, in_uri);
} else {
- debug_printf (1, "<%s> => <%s>? ", in_uri,
+ debug_printf (1, "<%s> => <%s>\n", in_uri,
out_uri ? out_uri : "ERR");
uri = soup_uri_new (in_uri);
}
if (!uri) {
- if (out_uri) {
- debug_printf (1, "ERR\n Could not parse %s\n", in_uri);
- return FALSE;
- } else {
- debug_printf (1, "OK\n");
- return TRUE;
- }
+ g_assert_null (out_uri);
+ return;
}
if (bits != NULL) {
- gboolean failed = FALSE;
-
- if (!test_cmpstr (uri->scheme, bits->scheme))
- failed = TRUE;
+ g_assert_cmpstr (uri->scheme, ==, bits->scheme);
+ g_assert_cmpstr (uri->user, ==, bits->user);
+ g_assert_cmpstr (uri->password, ==, bits->password);
+ g_assert_cmpstr (uri->host, ==, bits->host);
+ g_assert_cmpuint (uri->port, ==, bits->port);
+ g_assert_cmpstr (uri->path, ==, bits->path);
+ g_assert_cmpstr (uri->query, ==, bits->query);
+ g_assert_cmpstr (uri->fragment, ==, bits->fragment);
+ }
- if (!test_cmpstr (uri->user, bits->user))
- failed = TRUE;
+ uri_string = soup_uri_to_string (uri, FALSE);
+ soup_uri_free (uri);
- if (!test_cmpstr (uri->password, bits->password))
- failed = TRUE;
+ g_assert_cmpstr (uri_string, ==, out_uri);
+ g_free (uri_string);
+}
- if (!test_cmpstr (uri->host, bits->host))
- failed = TRUE;
+static void
+do_absolute_uri_tests (void)
+{
+ int i;
- if (uri->port != bits->port) {
- debug_printf (1, "ERR\n port was %u, expected %u\n",
- uri->port, bits->port);
- failed = TRUE;
- }
+ for (i = 0; i < num_abs_tests; i++) {
+ if (abs_tests[i].bugref)
+ g_test_bug (abs_tests[i].bugref);
+ do_uri (NULL, NULL, abs_tests[i].uri_string,
+ abs_tests[i].result, &abs_tests[i].bits);
+ }
+}
- if (!test_cmpstr (uri->path, bits->path))
- failed = TRUE;
+static void
+do_relative_uri_tests (void)
+{
+ SoupURI *base_uri;
+ char *uri_string;
+ int i;
- if (!test_cmpstr (uri->query, bits->query))
- failed = TRUE;
+ base_uri = soup_uri_new (base);
+ if (!base_uri) {
+ g_printerr ("Could not parse %s!\n", base);
+ exit (1);
+ }
- if (!test_cmpstr (uri->fragment, bits->fragment))
- failed = TRUE;
+ uri_string = soup_uri_to_string (base_uri, FALSE);
+ g_assert_cmpstr (uri_string, ==, base);
+ g_free (uri_string);
- if (failed)
- return FALSE;
+ for (i = 0; i < num_rel_tests; i++) {
+ do_uri (base_uri, base, rel_tests[i].uri_string,
+ rel_tests[i].result, &rel_tests[i].bits);
}
+ soup_uri_free (base_uri);
+}
- uri_string = soup_uri_to_string (uri, FALSE);
- soup_uri_free (uri);
+static void
+do_equality_tests (void)
+{
+ SoupURI *uri1, *uri2;
+ int i;
- if (!out_uri) {
- debug_printf (1, "ERR\n Got %s\n", uri_string);
- return FALSE;
- }
+ for (i = 0; i < num_eq_tests; i++) {
+ if (eq_tests[i].bugref)
+ g_test_bug (eq_tests[i].bugref);
- if (strcmp (uri_string, out_uri) != 0) {
- debug_printf (1, "NO\n Unparses to <%s>\n", uri_string);
- g_free (uri_string);
- return FALSE;
- }
- g_free (uri_string);
+ uri1 = soup_uri_new (eq_tests[i].one);
+ uri2 = soup_uri_new (eq_tests[i].two);
- debug_printf (1, "OK\n");
- return TRUE;
+ debug_printf (1, "<%s> == <%s>\n", eq_tests[i].one, eq_tests[i].two);
+ g_assert_true (soup_uri_equal (uri1, uri2));
+
+ soup_uri_free (uri1);
+ soup_uri_free (uri2);
+ }
}
static void
@@ -353,146 +371,83 @@ do_soup_uri_null_tests (void)
SoupURI *uri, *uri2;
char *uri_string;
- debug_printf (1, "\nsoup_uri_new (NULL)\n");
+ g_test_bug ("667637");
+ g_test_bug ("670431");
+
uri = soup_uri_new (NULL);
- if (SOUP_URI_IS_VALID (uri) || SOUP_URI_VALID_FOR_HTTP (uri)) {
- debug_printf (1, " ERROR: soup_uri_new(NULL) returns valid URI?\n");
- errors++;
- }
+ g_assert_false (SOUP_URI_IS_VALID (uri));
+ g_assert_false (SOUP_URI_VALID_FOR_HTTP (uri));
/* This implicitly also verifies that none of these methods g_warn */
- if (soup_uri_get_scheme (uri) ||
- soup_uri_get_user (uri) ||
- soup_uri_get_password (uri) ||
- soup_uri_get_host (uri) ||
- soup_uri_get_port (uri) ||
- soup_uri_get_path (uri) ||
- soup_uri_get_query (uri) ||
- soup_uri_get_fragment (uri)) {
- debug_printf (1, " ERROR: soup_uri_new(NULL) returns non-empty URI?\n");
- errors++;
- }
-
- expect_warning = TRUE;
+ g_assert_null (soup_uri_get_scheme (uri));
+ g_assert_null (soup_uri_get_user (uri));
+ g_assert_null (soup_uri_get_password (uri));
+ g_assert_null (soup_uri_get_host (uri));
+ g_assert_cmpint (soup_uri_get_port (uri), ==, 0);
+ g_assert_null (soup_uri_get_path (uri));
+ g_assert_null (soup_uri_get_query (uri));
+ g_assert_null (soup_uri_get_fragment (uri));
+
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_CRITICAL,
+ "*base == NULL*");
uri2 = soup_uri_new_with_base (uri, "/path");
- if (uri2 || expect_warning) {
- debug_printf (1, " ERROR: soup_uri_new_with_base didn't fail on NULL URI?\n");
- errors++;
- expect_warning = FALSE;
- }
+ g_test_assert_expected_messages ();
+ g_assert_null (uri2);
- expect_warning = TRUE;
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*SOUP_URI_IS_VALID*");
uri_string = soup_uri_to_string (uri, FALSE);
- if (expect_warning) {
- debug_printf (1, " ERROR: soup_uri_to_string didn't fail on NULL URI?\n");
- errors++;
- expect_warning = FALSE;
- } else if (*uri_string) {
- debug_printf (1, " ERROR: soup_uri_to_string on NULL URI returned '%s'\n",
- uri_string);
- errors++;
- }
+ g_test_assert_expected_messages ();
+ g_assert_cmpstr (uri_string, ==, "");
g_free (uri_string);
soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
- if (SOUP_URI_IS_VALID (uri) || SOUP_URI_VALID_FOR_HTTP (uri)) {
- debug_printf (1, " ERROR: setting scheme on NULL URI makes it valid?\n");
- errors++;
- }
+ g_assert_false (SOUP_URI_IS_VALID (uri));
+ g_assert_false (SOUP_URI_VALID_FOR_HTTP (uri));
- expect_warning = TRUE;
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*SOUP_URI_IS_VALID*");
uri_string = soup_uri_to_string (uri, FALSE);
- if (expect_warning) {
- debug_printf (1, " ERROR: soup_uri_to_string didn't fail on scheme-only URI?\n");
- errors++;
- expect_warning = FALSE;
- } else if (strcmp (uri_string, "http:") != 0) {
- debug_printf (1, " ERROR: soup_uri_to_string returned '%s' instead of 'http:'\n",
- uri_string);
- errors++;
- }
+ g_test_assert_expected_messages ();
+ g_assert_cmpstr (uri_string, ==, "http:");
g_free (uri_string);
soup_uri_set_host (uri, "localhost");
- if (SOUP_URI_IS_VALID (uri)) {
- debug_printf (1, " ERROR: setting scheme+host on NULL URI makes it valid?\n");
- errors++;
- }
- if (SOUP_URI_VALID_FOR_HTTP (uri)) {
- debug_printf (1, " ERROR: setting scheme+host on NULL URI makes it valid for http?\n");
- errors++;
- }
+ g_assert_false (SOUP_URI_IS_VALID (uri));
+ g_assert_false (SOUP_URI_VALID_FOR_HTTP (uri));
- expect_warning = TRUE;
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*SOUP_URI_IS_VALID*");
uri_string = soup_uri_to_string (uri, FALSE);
- if (expect_warning) {
- debug_printf (1, " ERROR: soup_uri_to_string didn't fail on scheme+host URI?\n");
- errors++;
- expect_warning = FALSE;
- } else if (strcmp (uri_string, "http://localhost/") != 0) {
- debug_printf (1, " ERROR: soup_uri_to_string with NULL path returned '%s' instead of 'http://localhost/'\n",
- uri_string);
- errors++;
- }
+ g_test_assert_expected_messages ();
+ g_assert_cmpstr (uri_string, ==, "http://localhost/");
g_free (uri_string);
- expect_warning = TRUE;
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*SOUP_URI_IS_VALID*");
uri2 = soup_uri_new_with_base (uri, "/path");
- if (expect_warning) {
- debug_printf (1, " ERROR: soup_uri_new_with_base didn't warn on NULL+scheme URI?\n");
- errors++;
- expect_warning = FALSE;
- } else if (!uri2) {
- debug_printf (1, " ERROR: soup_uri_new_with_base didn't fix path on NULL+scheme URI\n");
- errors++;
- }
+ g_test_assert_expected_messages ();
+ g_assert_true (uri2 != NULL);
if (uri2) {
uri_string = soup_uri_to_string (uri2, FALSE);
- if (!uri_string) {
- debug_printf (1, " ERROR: soup_uri_to_string failed on uri2?\n");
- errors++;
- } else if (strcmp (uri_string, "http://localhost/path") != 0) {
- debug_printf (1, " ERROR: soup_uri_to_string returned '%s' instead of 'http://localhost/path'\n",
- uri_string);
- errors++;
- }
+ g_assert_cmpstr (uri_string, ==, "http://localhost/path");
g_free (uri_string);
soup_uri_free (uri2);
}
- expect_warning = TRUE;
+ g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING,
+ "*path != NULL*");
soup_uri_set_path (uri, NULL);
- if (expect_warning) {
- debug_printf (1, " ERROR: setting path to NULL doesn't warn\n");
- errors++;
- expect_warning = FALSE;
- }
- if (!uri->path || *uri->path) {
- debug_printf (1, " ERROR: setting path to NULL != \"\"\n");
- errors++;
- soup_uri_set_path (uri, "");
- }
+ g_test_assert_expected_messages ();
+ g_assert_cmpstr (uri->path, ==, "");
uri_string = soup_uri_to_string (uri, FALSE);
- if (!uri_string) {
- debug_printf (1, " ERROR: soup_uri_to_string failed on complete URI?\n");
- errors++;
- } else if (strcmp (uri_string, "http://localhost/") != 0) {
- debug_printf (1, " ERROR: soup_uri_to_string with empty path returned '%s' instead of 'http://localhost/'\n",
- uri_string);
- errors++;
- }
+ g_assert_cmpstr (uri_string, ==, "http://localhost/");
g_free (uri_string);
- if (!SOUP_URI_IS_VALID (uri)) {
- debug_printf (1, " ERROR: setting scheme+path on NULL URI doesn't make it valid?\n");
- errors++;
- }
- if (!SOUP_URI_VALID_FOR_HTTP (uri)) {
- debug_printf (1, " ERROR: setting scheme+host+path on NULL URI doesn't make it valid for http?\n");
- errors++;
- }
+ g_assert_true (SOUP_URI_IS_VALID (uri));
+ g_assert_true (SOUP_URI_VALID_FOR_HTTP (uri));
soup_uri_free (uri);
}
@@ -517,29 +472,23 @@ do_normalization_tests (void)
char *normalized;
int i;
- debug_printf (1, "\nsoup_uri_normalize\n");
+ g_test_bug ("680018");
for (i = 0; i < num_normalization_tests; i++) {
if (normalization_tests[i].unescape_extra) {
- debug_printf (1, "<%s> unescaping <%s> => <%s>: ",
+ debug_printf (1, "<%s> unescaping <%s> => <%s>\n",
normalization_tests[i].uri_string,
normalization_tests[i].unescape_extra,
normalization_tests[i].result);
} else {
- debug_printf (1, "<%s> => <%s>: ",
+ debug_printf (1, "<%s> => <%s>\n",
normalization_tests[i].uri_string,
normalization_tests[i].result);
}
normalized = soup_uri_normalize (normalization_tests[i].uri_string,
normalization_tests[i].unescape_extra);
-
- if (!strcmp (normalized, normalization_tests[i].result))
- debug_printf (1, "OK\n");
- else {
- debug_printf (1, "NO, got <%s>\n", normalized);
- errors++;
- }
+ g_assert_cmpstr (normalized, ==, normalization_tests[i].result);
g_free (normalized);
}
}
@@ -547,62 +496,18 @@ do_normalization_tests (void)
int
main (int argc, char **argv)
{
- SoupURI *base_uri, *uri1, *uri2;
- char *uri_string;
- int i;
+ int ret;
test_init (argc, argv, NULL);
- debug_printf (1, "Absolute URI parsing\n");
- for (i = 0; i < num_abs_tests; i++) {
- if (!do_uri (NULL, NULL, abs_tests[i].uri_string,
- abs_tests[i].result, &abs_tests[i].bits))
- errors++;
- }
-
- debug_printf (1, "\nRelative URI parsing\n");
- base_uri = soup_uri_new (base);
- if (!base_uri) {
- g_printerr ("Could not parse %s!\n", base);
- exit (1);
- }
-
- uri_string = soup_uri_to_string (base_uri, FALSE);
- if (strcmp (uri_string, base) != 0) {
- g_printerr ("URI <%s> unparses to <%s>\n",
- base, uri_string);
- errors++;
- }
- g_free (uri_string);
-
- for (i = 0; i < num_rel_tests; i++) {
- if (!do_uri (base_uri, base, rel_tests[i].uri_string,
- rel_tests[i].result, &rel_tests[i].bits))
- errors++;
- }
- soup_uri_free (base_uri);
-
- debug_printf (1, "\nURI equality testing\n");
- for (i = 0; i < num_eq_tests; i++) {
- uri1 = soup_uri_new (eq_tests[i].one);
- uri2 = soup_uri_new (eq_tests[i].two);
- debug_printf (1, "<%s> == <%s>? ", eq_tests[i].one, eq_tests[i].two);
- if (soup_uri_equal (uri1, uri2))
- debug_printf (1, "OK\n");
- else {
- debug_printf (1, "NO\n");
- debug_printf (1, "%s : %s : %s\n%s : %s : %s\n",
- uri1->scheme, uri1->host, uri1->path,
- uri2->scheme, uri2->host, uri2->path);
- errors++;
- }
- soup_uri_free (uri1);
- soup_uri_free (uri2);
- }
+ g_test_add_func ("/uri/absolute", do_absolute_uri_tests);
+ g_test_add_func ("/uri/relative", do_relative_uri_tests);
+ 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);
- do_soup_uri_null_tests ();
- do_normalization_tests ();
+ ret = g_test_run ();
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c
index 8ad2187b..bfeb200a 100644
--- a/tests/xmlrpc-server-test.c
+++ b/tests/xmlrpc-server-test.c
@@ -5,12 +5,12 @@
#include "test-utils.h"
+static char *uri;
+
#ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
#endif
-GMainLoop *loop;
-
static void
type_error (SoupMessage *msg, GType expected, GValueArray *params, int bad_value)
{
@@ -180,7 +180,17 @@ do_echo (SoupMessage *msg, GValueArray *params)
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
@@ -216,6 +226,8 @@ server_callback (SoupServer *server, SoupMessage *msg,
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);
@@ -225,73 +237,64 @@ server_callback (SoupServer *server, SoupMessage *msg,
g_value_array_free (params);
}
-static void
-xmlrpc_test_exited (GPid pid, int status, gpointer data)
-{
- errors = WIFEXITED (status) ? WEXITSTATUS (status) : 1;
- g_main_loop_quit (loop);
-}
-
static gboolean
-xmlrpc_test_print (GIOChannel *io, GIOCondition cond, gpointer data)
+run_xmlrpc_test (char **argv,
+ char **stdout_out,
+ char **stderr_out,
+ GError **error)
{
- char *line;
- gsize len;
- GIOStatus status;
+ gboolean ok;
+ int status;
- if (!(cond & G_IO_IN))
- return FALSE;
+ argv[0] = g_test_build_filename (G_TEST_BUILT, "xmlrpc-test", NULL);
+ ok = g_spawn_sync (NULL, argv, NULL, 0, NULL, NULL,
+ stdout_out, stderr_out, &status,
+ error);
+ g_free (argv[0]);
- status = g_io_channel_read_line (io, &line, &len, NULL, NULL);
- if (status == G_IO_STATUS_NORMAL) {
- /* Don't print the exit status, just the debug stuff */
- if (strncmp (line, "xmlrpc-test:", strlen ("xmlrpc-test:")) != 0)
- g_print ("%s", line);
- g_free (line);
- return TRUE;
- } else if (status == G_IO_STATUS_AGAIN)
- return TRUE;
- else
+ if (!ok)
return FALSE;
+
+ return g_spawn_check_exit_status (status, error);
}
static void
-do_xmlrpc_tests (SoupURI *uri)
+do_one_xmlrpc_test (gconstpointer data)
{
- char *argv[8];
- int arg, out;
- gboolean ok;
- GPid pid;
+ const char *path = data;
+ char *argv[12];
+ char *stdout_out, *stderr_out;
GError *error = NULL;
- GIOChannel *child_out;
+ int arg;
- argv[0] = "./xmlrpc-test";
- argv[1] = "-s";
- argv[2] = "-u";
- argv[3] = soup_uri_to_string (uri, FALSE);
+ 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 + 4] = "-d";
- argv[arg + 4] = NULL;
-
- ok = g_spawn_async_with_pipes (NULL, argv, NULL,
- G_SPAWN_DO_NOT_REAP_CHILD,
- NULL, NULL, &pid,
- NULL, &out, NULL,
- &error);
- g_free (argv[3]);
-
- if (!ok) {
- g_print ("Could not run xmlrpc-test: %s\n", error->message);
- errors++;
- return;
+ 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);
}
- g_child_watch_add (pid, xmlrpc_test_exited, NULL);
- child_out = g_io_channel_unix_new (out);
- g_io_add_watch (child_out, G_IO_IN | G_IO_ERR | G_IO_HUP,
- xmlrpc_test_print, NULL);
- g_io_channel_unref (child_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;
@@ -307,29 +310,61 @@ int
main (int argc, char **argv)
{
SoupServer *server;
- SoupURI *uri;
+ int ret;
test_init (argc, argv, no_test_entry);
- server = soup_test_server_new (FALSE);
+ server = soup_test_server_new (run_tests);
soup_server_add_handler (server, "/xmlrpc-server.php",
server_callback, NULL, NULL);
-
- loop = g_main_loop_new (NULL, TRUE);
+ uri = g_strdup_printf ("http://127.0.0.1:%u/xmlrpc-server.php",
+ soup_server_get_port (server));
if (run_tests) {
- uri = soup_uri_new ("http://127.0.0.1/xmlrpc-server.php");
- soup_uri_set_port (uri, soup_server_get_port (server));
- do_xmlrpc_tests (uri);
- soup_uri_free (uri);
- } else
+ 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-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/"));
+ path = g_strdup_printf ("/xmlrpc-server/%s", tests[i] + strlen ("/xmlrpc/"));
+ 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", soup_server_get_port (server));
- g_main_loop_run (loop);
- g_main_loop_unref (loop);
+ 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);
+ g_free (uri);
if (run_tests)
test_cleanup ();
- return errors != 0;
+ return ret;
}
diff --git a/tests/xmlrpc-server.php b/tests/xmlrpc-server.php
index 2e3dd381..66cb2be7 100644
--- a/tests/xmlrpc-server.php
+++ b/tests/xmlrpc-server.php
@@ -2,13 +2,13 @@
function paramfault ()
{
- # xmlrpc-epi-php translates this into a real <fault>
+ # xmlrpc-php translates this into a real <fault>
$fault["faultCode"] = -32602;
$fault["faultString"] = "bad parameter";
return $fault;
}
-# We only check the params in sum(), because that's the one that
+# We only check the params in sum(), because that is the one that
# xmlrpc-test tests will fail if given bad args
function sum ($method_name, $params, $app_data)
@@ -69,8 +69,16 @@ function echo_ ($method_name, $params, $app_data)
return $params[0];
}
-# Work around xmlrpc-epi-php lossage; otherwise the datetime values
-# we return will sometimes get a DST adjustment we don't want.
+function ping ($method_name, $params, $app_data)
+{
+ if (count ($params) == 0)
+ return "pong";
+ else
+ return paramfault ();
+}
+
+# Work around xmlrpc-php lossage; otherwise the datetime values
+# we return will sometimes get a DST adjustment we do not want.
putenv ("TZ=");
$xmlrpc_server = xmlrpc_server_create ();
@@ -79,6 +87,7 @@ xmlrpc_server_register_method($xmlrpc_server, "countBools", "countBools");
xmlrpc_server_register_method($xmlrpc_server, "md5sum", "md5sum");
xmlrpc_server_register_method($xmlrpc_server, "dateChange", "dateChange");
xmlrpc_server_register_method($xmlrpc_server, "echo", "echo_");
+xmlrpc_server_register_method($xmlrpc_server, "ping", "ping");
$response = xmlrpc_server_call_method ($xmlrpc_server,
implode("\r\n", file('php://input')),
diff --git a/tests/xmlrpc-test.c b/tests/xmlrpc-test.c
index ec67e4d1..f6b20b8f 100644
--- a/tests/xmlrpc-test.c
+++ b/tests/xmlrpc-test.c
@@ -14,6 +14,18 @@ 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 const char *const value_type[] = {
"BAD",
"int",
@@ -27,44 +39,26 @@ static const char *const value_type[] = {
};
static gboolean
-do_xmlrpc (const char *method, GValue *retval, ...)
+send_xmlrpc (const char *body, GValue *retval)
{
SoupMessage *msg;
- va_list args;
- GValueArray *params;
GError *err = NULL;
- char *body;
-
- 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;
msg = soup_message_new ("POST", uri);
- soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
+ soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY,
body, strlen (body));
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, "ERROR: %d %s\n", msg->status_code,
- msg->reason_phrase);
- g_object_unref (msg);
- return FALSE;
- }
+ 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\n", err->code, err->message);
+ soup_test_assert (FALSE, "FAULT: %d %s\n", err->code, err->message);
g_error_free (err);
} else
- debug_printf (1, "ERROR: could not parse response\n");
+ soup_test_assert (FALSE, "ERROR: could not parse response\n");
g_object_unref (msg);
return FALSE;
}
@@ -74,13 +68,36 @@ do_xmlrpc (const char *method, GValue *retval, ...)
}
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)) {
- debug_printf (1, "ERROR: could not parse response\n");
- g_value_unset (value);
+ g_assert_true (G_VALUE_HOLDS (value, type));
return FALSE;
}
@@ -90,7 +107,7 @@ check_xmlrpc (GValue *value, GType type, ...)
return TRUE;
}
-static gboolean
+static void
test_sum (void)
{
GValueArray *ints;
@@ -98,7 +115,9 @@ test_sum (void)
GValue retval;
gboolean ok;
- debug_printf (1, "sum (array of int -> int): ");
+ SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER;
+
+ debug_printf (2, "sum (array of int -> int): ");
ints = g_value_array_new (10);
for (i = sum = 0; i < 10; i++) {
@@ -116,14 +135,13 @@ test_sum (void)
g_value_array_free (ints);
if (!ok)
- return FALSE;
+ return;
- debug_printf (2, "%d: ", result);
- debug_printf (1, "%s\n", result == sum ? "OK!" : "WRONG!");
- return result == sum;
+ debug_printf (2, "%d\n", result);
+ g_assert_cmpint (result, ==, sum);
}
-static gboolean
+static void
test_countBools (void)
{
GValueArray *bools;
@@ -133,7 +151,9 @@ test_countBools (void)
gboolean val, ok;
GHashTable *result;
- debug_printf (1, "countBools (array of boolean -> struct of ints): ");
+ 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++) {
@@ -153,25 +173,19 @@ test_countBools (void)
check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result));
g_value_array_free (bools);
if (!ok)
- return FALSE;
+ 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));
- if (!soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)) {
- debug_printf (1, "NO 'true' value in response\n");
- return FALSE;
- }
- if (!soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)) {
- debug_printf (1, "NO 'false' value in response\n");
- return FALSE;
- }
g_hash_table_destroy (result);
- debug_printf (2, "{ true: %d, false: %d } ", ret_trues, ret_falses);
- ok = (trues == ret_trues) && (falses == ret_falses);
- debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
- return ok;
+ 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 gboolean
+static void
test_md5sum (void)
{
GByteArray *data, *result;
@@ -182,7 +196,9 @@ test_md5sum (void)
GValue retval;
gboolean ok;
- debug_printf (1, "md5sum (base64 -> base64): ");
+ 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);
@@ -200,21 +216,14 @@ test_md5sum (void)
check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result));
g_byte_array_free (data, TRUE);
if (!ok)
- return FALSE;
+ return;
- if (result->len != digest_len) {
- debug_printf (1, "result has WRONG length (%d)\n", result->len);
- g_byte_array_free (result, TRUE);
- return FALSE;
- }
-
- ok = (memcmp (digest, result->data, digest_len) == 0);
- debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
+ soup_assert_cmpmem (result->data, result->len,
+ digest, digest_len);
g_byte_array_free (result, TRUE);
- return ok;
}
-static gboolean
+static void
test_dateChange (void)
{
GHashTable *structval;
@@ -223,7 +232,9 @@ test_dateChange (void)
GValue retval;
gboolean ok;
- debug_printf (1, "dateChange (date, struct of ints -> time): ");
+ 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),
@@ -288,26 +299,24 @@ test_dateChange (void)
g_hash_table_destroy (structval);
if (!ok) {
soup_date_free (date);
- return FALSE;
+ return;
}
if (debug_level >= 2) {
timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC);
- debug_printf (2, "%s: ", timestamp);
+ debug_printf (2, "%s\n", timestamp);
g_free (timestamp);
}
- ok = ((date->year == result->year) &&
- (date->month == result->month) &&
- (date->day == result->day) &&
- (date->hour == result->hour) &&
- (date->minute == result->minute) &&
- (date->second == result->second));
+ 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);
-
- debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
- return ok;
}
static const char *const echo_strings[] = {
@@ -325,15 +334,16 @@ static const char *const echo_strings_broken[] = {
"amp; so is lt;thisgt;"
};
-static gboolean
+static void
test_echo (void)
{
GValueArray *originals, *echoes;
GValue retval;
int i;
- gboolean php_bug = FALSE;
- debug_printf (1, "echo (array of string -> array of string): ");
+ 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++) {
@@ -347,7 +357,7 @@ test_echo (void)
G_TYPE_INVALID) &&
check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) {
g_value_array_free (originals);
- return FALSE;
+ return;
}
g_value_array_free (originals);
@@ -356,36 +366,71 @@ test_echo (void)
debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ",
g_value_get_string (&echoes->values[i]));
}
- debug_printf (2, "] -> ");
+ debug_printf (2, "]\n");
}
- if (echoes->n_values != N_ECHO_STRINGS) {
- debug_printf (1, " WRONG! Wrong number of return strings");
- g_value_array_free (echoes);
- return FALSE;
- }
+ g_assert_cmpint (echoes->n_values, ==, N_ECHO_STRINGS);
for (i = 0; i < echoes->n_values; i++) {
- if (strcmp (echo_strings[i], g_value_get_string (&echoes->values[i])) != 0) {
- if (!server_test && strcmp (echo_strings_broken[i], g_value_get_string (&echoes->values[i])) == 0)
- php_bug = TRUE;
- else {
- debug_printf (1, " WRONG! Mismatch at %d\n", i + 1);
- g_value_array_free (echoes);
- return FALSE;
- }
+ 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]));
}
- if (php_bug)
- debug_printf (1, "WRONG, but it's php's fault\n");
- else
- debug_printf (1, "OK!\n");
g_value_array_free (echoes);
- return TRUE;
}
-static gboolean
+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;
@@ -397,12 +442,7 @@ do_bad_xmlrpc (const char *body)
body, strlen (body));
soup_session_send_message (session, msg);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- debug_printf (1, "ERROR: %d %s\n", msg->status_code,
- msg->reason_phrase);
- g_object_unref (msg);
- return FALSE;
- }
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
msg->response_body->length,
@@ -412,44 +452,43 @@ do_bad_xmlrpc (const char *body)
err->code, err->message);
g_error_free (err);
g_object_unref (msg);
- return TRUE;
+ return;
} else
- debug_printf (1, "ERROR: could not parse response\n");
+ soup_test_assert (FALSE, "ERROR: could not parse response\n");
} else
- debug_printf (1, "Unexpectedly got successful response!\n");
+ soup_test_assert (FALSE, "Unexpectedly got successful response!\n");
g_object_unref (msg);
- return FALSE;
}
-static gboolean
+static void
test_fault_malformed (void)
{
- debug_printf (1, "malformed request: ");
+ SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER;
- return do_bad_xmlrpc ("<methodCall/>");
+ do_bad_xmlrpc ("<methodCall/>");
}
-static gboolean
+static void
test_fault_method (void)
{
- debug_printf (1, "request to non-existent method: ");
+ SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER;
- return do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
+ do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
}
-static gboolean
+static void
test_fault_args (void)
{
- debug_printf (1, "request with invalid args: ");
+ SOUP_TEST_SKIP_IF_NO_XMLRPC_SERVER;
- return do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
+ 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,
+ { "uri", 'U', 0, G_OPTION_ARG_STRING, &uri,
"Alternate URI for server", NULL },
- { "server-test", 's', 0, G_OPTION_ARG_NONE, &server_test,
+ { "server-test", 'S', 0, G_OPTION_ARG_NONE, &server_test,
"If this is being run from xmlrpc-server-test", NULL },
{ NULL }
};
@@ -457,34 +496,32 @@ static GOptionEntry xmlrpc_entries[] = {
int
main (int argc, char **argv)
{
+ int ret;
+
test_init (argc, argv, xmlrpc_entries);
- if (!uri) {
+ if (!uri && !server_test) {
apache_init ();
uri = default_uri;
}
session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
- if (!test_sum ())
- errors++;
- if (!test_countBools ())
- errors++;
- if (!test_md5sum ())
- errors++;
- if (!test_dateChange ())
- errors++;
- if (!test_echo ())
- errors++;
- if (!test_fault_malformed ())
- errors++;
- if (!test_fault_method ())
- errors++;
- if (!test_fault_args ())
- errors++;
+ 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);
+
+ ret = g_test_run ();
soup_test_session_abort_unref (session);
test_cleanup ();
- return errors != 0;
+ return ret;
}