summaryrefslogtreecommitdiff
path: root/libsoup
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup')
-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
128 files changed, 11007 insertions, 6761 deletions
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: