1 module soup.Message; 2 3 private import gio.IOStream; 4 private import gio.TlsCertificate; 5 private import glib.ConstructionException; 6 private import glib.HashTable; 7 private import glib.Str; 8 private import gobject.ObjectG; 9 private import gobject.Signals; 10 private import soup.Address; 11 private import soup.Buffer; 12 private import soup.Request; 13 private import soup.URI; 14 private import soup.c.functions; 15 public import soup.c.types; 16 private import std.algorithm; 17 18 19 /** 20 * A #SoupMessage represents an HTTP message that is being sent or 21 * received. 22 * 23 * For client-side usage, if you are using the traditional 24 * #SoupSession APIs (soup_session_queue_message() and 25 * soup_session_send_message()), you would create a #SoupMessage with 26 * soup_message_new() or soup_message_new_from_uri(), set up its 27 * fields appropriately, and send it. If you are using the newer 28 * #SoupRequest API, you would create a request with 29 * soup_session_request_http() or soup_session_request_http_uri(), and 30 * the returned #SoupRequestHTTP will already have an associated 31 * #SoupMessage that you can retrieve via 32 * soup_request_http_get_message(). 33 * 34 * For server-side usage, #SoupServer will create #SoupMessage<!-- 35 * -->s automatically for incoming requests, which your application 36 * will receive via handlers. 37 * 38 * Note that libsoup's terminology here does not quite match the HTTP 39 * specification: in RFC 2616, an "HTTP-message" is 40 * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a 41 * Response. In libsoup, a #SoupMessage combines both the request and 42 * the response. 43 */ 44 public class Message : ObjectG 45 { 46 /** the main Gtk struct */ 47 protected SoupMessage* soupMessage; 48 49 /** Get the main Gtk struct */ 50 public SoupMessage* getMessageStruct(bool transferOwnership = false) 51 { 52 if (transferOwnership) 53 ownedRef = false; 54 return soupMessage; 55 } 56 57 /** the main Gtk struct as a void* */ 58 protected override void* getStruct() 59 { 60 return cast(void*)soupMessage; 61 } 62 63 /** 64 * Sets our main struct and passes it to the parent class. 65 */ 66 public this (SoupMessage* soupMessage, bool ownedRef = false) 67 { 68 this.soupMessage = soupMessage; 69 super(cast(GObject*)soupMessage, ownedRef); 70 } 71 72 73 /** */ 74 public static GType getType() 75 { 76 return soup_message_get_type(); 77 } 78 79 /** 80 * Creates a new empty #SoupMessage, which will connect to @uri 81 * 82 * Params: 83 * method = the HTTP method for the created request 84 * uriString = the destination endpoint (as a string) 85 * 86 * Returns: the new #SoupMessage (or %NULL if @uri 87 * could not be parsed). 88 * 89 * Throws: ConstructionException GTK+ fails to create the object. 90 */ 91 public this(string method, string uriString) 92 { 93 auto __p = soup_message_new(Str.toStringz(method), Str.toStringz(uriString)); 94 95 if(__p is null) 96 { 97 throw new ConstructionException("null returned by new"); 98 } 99 100 this(cast(SoupMessage*) __p, true); 101 } 102 103 /** 104 * Creates a new empty #SoupMessage, which will connect to @uri 105 * 106 * Params: 107 * method = the HTTP method for the created request 108 * uri = the destination endpoint (as a #SoupURI) 109 * 110 * Returns: the new #SoupMessage 111 * 112 * Throws: ConstructionException GTK+ fails to create the object. 113 */ 114 public this(string method, URI uri) 115 { 116 auto __p = soup_message_new_from_uri(Str.toStringz(method), (uri is null) ? null : uri.getURIStruct()); 117 118 if(__p is null) 119 { 120 throw new ConstructionException("null returned by new_from_uri"); 121 } 122 123 this(cast(SoupMessage*) __p, true); 124 } 125 126 /** 127 * Adds a signal handler to @msg for @signal, as with 128 * g_signal_connect(), but the @callback will only be run if @msg's 129 * incoming messages headers (that is, the 130 * <literal>request_headers</literal> for a client #SoupMessage, or 131 * the <literal>response_headers</literal> for a server #SoupMessage) 132 * contain a header named @header. 133 * 134 * Params: 135 * signal = signal to connect the handler to. 136 * header = HTTP response header to match against 137 * callback = the header handler 138 * userData = data to pass to @handler_cb 139 * 140 * Returns: the handler ID from g_signal_connect() 141 */ 142 public uint addHeaderHandler(string signal, string header, GCallback callback, void* userData) 143 { 144 return soup_message_add_header_handler(soupMessage, Str.toStringz(signal), Str.toStringz(header), callback, userData); 145 } 146 147 /** 148 * Adds a signal handler to @msg for @signal, as with 149 * g_signal_connect(), but the @callback will only be run if @msg has 150 * the status @status_code. 151 * 152 * @signal must be a signal that will be emitted after @msg's status 153 * is set. For a client #SoupMessage, this means it can't be a "wrote" 154 * signal. For a server #SoupMessage, this means it can't be a "got" 155 * signal. 156 * 157 * Params: 158 * signal = signal to connect the handler to. 159 * statusCode = status code to match against 160 * callback = the header handler 161 * userData = data to pass to @handler_cb 162 * 163 * Returns: the handler ID from g_signal_connect() 164 */ 165 public uint addStatusCodeHandler(string signal, uint statusCode, GCallback callback, void* userData) 166 { 167 return soup_message_add_status_code_handler(soupMessage, Str.toStringz(signal), statusCode, callback, userData); 168 } 169 170 /** */ 171 public void contentSniffed(string contentType, HashTable params) 172 { 173 soup_message_content_sniffed(soupMessage, Str.toStringz(contentType), (params is null) ? null : params.getHashTableStruct()); 174 } 175 176 /** 177 * This disables the actions of #SoupSessionFeature<!-- -->s with the 178 * given @feature_type (or a subclass of that type) on @msg, so that 179 * @msg is processed as though the feature(s) hadn't been added to the 180 * session. Eg, passing #SOUP_TYPE_CONTENT_SNIFFER for @feature_type 181 * will disable Content-Type sniffing on the message. 182 * 183 * You must call this before queueing @msg on a session; calling it on 184 * a message that has already been queued is undefined. In particular, 185 * you cannot call this on a message that is being requeued after a 186 * redirect or authentication. 187 * 188 * Params: 189 * featureType = the #GType of a #SoupSessionFeature 190 * 191 * Since: 2.28 192 */ 193 public void disableFeature(GType featureType) 194 { 195 soup_message_disable_feature(soupMessage, featureType); 196 } 197 198 /** */ 199 public void finished() 200 { 201 soup_message_finished(soupMessage); 202 } 203 204 /** 205 * Gets the address @msg's URI points to. After first setting the 206 * URI on a message, this will be unresolved, although the message's 207 * session will resolve it before sending the message. 208 * 209 * Returns: the address @msg's URI points to 210 * 211 * Since: 2.26 212 */ 213 public Address getAddress() 214 { 215 auto __p = soup_message_get_address(soupMessage); 216 217 if(__p is null) 218 { 219 return null; 220 } 221 222 return ObjectG.getDObject!(Address)(cast(SoupAddress*) __p); 223 } 224 225 /** 226 * Gets @msg's first-party #SoupURI 227 * 228 * Returns: the @msg's first party #SoupURI 229 * 230 * Since: 2.30 231 */ 232 public URI getFirstParty() 233 { 234 auto __p = soup_message_get_first_party(soupMessage); 235 236 if(__p is null) 237 { 238 return null; 239 } 240 241 return ObjectG.getDObject!(URI)(cast(SoupURI*) __p); 242 } 243 244 /** 245 * Gets the flags on @msg 246 * 247 * Returns: the flags 248 */ 249 public SoupMessageFlags getFlags() 250 { 251 return soup_message_get_flags(soupMessage); 252 } 253 254 /** 255 * Gets the HTTP version of @msg. This is the minimum of the 256 * version from the request and the version from the response. 257 * 258 * Returns: the HTTP version 259 */ 260 public SoupHTTPVersion getHttpVersion() 261 { 262 return soup_message_get_http_version(soupMessage); 263 } 264 265 /** 266 * If @msg is using https (or attempted to use https but got 267 * %SOUP_STATUS_SSL_FAILED), this retrieves the #GTlsCertificate 268 * associated with its connection, and the #GTlsCertificateFlags 269 * showing what problems, if any, have been found with that 270 * certificate. 271 * 272 * <note><para>This is only meaningful with messages processed by a #SoupSession and is 273 * not useful for messages received by a #SoupServer</para></note> 274 * 275 * Params: 276 * certificate = @msg's TLS certificate 277 * errors = the verification status of @certificate 278 * 279 * Returns: %TRUE if @msg used/attempted https, %FALSE if not 280 * 281 * Since: 2.34 282 */ 283 public bool getHttpsStatus(out TlsCertificate certificate, out GTlsCertificateFlags errors) 284 { 285 GTlsCertificate* outcertificate = null; 286 287 auto __p = soup_message_get_https_status(soupMessage, &outcertificate, &errors) != 0; 288 289 certificate = ObjectG.getDObject!(TlsCertificate)(outcertificate); 290 291 return __p; 292 } 293 294 /** */ 295 public bool getIsTopLevelNavigation() 296 { 297 return soup_message_get_is_top_level_navigation(soupMessage) != 0; 298 } 299 300 /** 301 * Retrieves the #SoupMessagePriority. If not set this value defaults 302 * to #SOUP_MESSAGE_PRIORITY_NORMAL. 303 * 304 * Returns: the priority of the message. 305 * 306 * Since: 2.44 307 */ 308 public SoupMessagePriority getPriority() 309 { 310 return soup_message_get_priority(soupMessage); 311 } 312 313 /** 314 * Gets @msg's site for cookies #SoupURI 315 * 316 * Returns: the @msg's site for cookies #SoupURI 317 * 318 * Since: 2.70 319 */ 320 public URI getSiteForCookies() 321 { 322 auto __p = soup_message_get_site_for_cookies(soupMessage); 323 324 if(__p is null) 325 { 326 return null; 327 } 328 329 return ObjectG.getDObject!(URI)(cast(SoupURI*) __p); 330 } 331 332 /** 333 * If @msg is associated with a #SoupRequest, this returns that 334 * request. Otherwise it returns %NULL. 335 * 336 * Returns: @msg's associated #SoupRequest 337 * 338 * Since: 2.42 339 */ 340 public Request getSoupRequest() 341 { 342 auto __p = soup_message_get_soup_request(soupMessage); 343 344 if(__p is null) 345 { 346 return null; 347 } 348 349 return ObjectG.getDObject!(Request)(cast(SoupRequest*) __p); 350 } 351 352 /** 353 * Gets @msg's URI 354 * 355 * Returns: the URI @msg is targeted for. 356 */ 357 public URI getUri() 358 { 359 auto __p = soup_message_get_uri(soupMessage); 360 361 if(__p is null) 362 { 363 return null; 364 } 365 366 return ObjectG.getDObject!(URI)(cast(SoupURI*) __p); 367 } 368 369 /** */ 370 public void gotBody() 371 { 372 soup_message_got_body(soupMessage); 373 } 374 375 /** */ 376 public void gotChunk(Buffer chunk) 377 { 378 soup_message_got_chunk(soupMessage, (chunk is null) ? null : chunk.getBufferStruct()); 379 } 380 381 /** */ 382 public void gotHeaders() 383 { 384 soup_message_got_headers(soupMessage); 385 } 386 387 /** */ 388 public void gotInformational() 389 { 390 soup_message_got_informational(soupMessage); 391 } 392 393 /** 394 * Get whether #SoupSessionFeature<!-- -->s of the given @feature_type 395 * (or a subclass of that type) are disabled on @msg. 396 * See soup_message_disable_feature(). 397 * 398 * Params: 399 * featureType = the #GType of a #SoupSessionFeature 400 * 401 * Returns: %TRUE if feature is disabled, or %FALSE otherwise. 402 * 403 * Since: 2.72 404 */ 405 public bool isFeatureDisabled(GType featureType) 406 { 407 return soup_message_is_feature_disabled(soupMessage, featureType) != 0; 408 } 409 410 /** 411 * Determines whether or not @msg's connection can be kept alive for 412 * further requests after processing @msg, based on the HTTP version, 413 * Connection header, etc. 414 * 415 * Returns: %TRUE or %FALSE. 416 */ 417 public bool isKeepalive() 418 { 419 return soup_message_is_keepalive(soupMessage) != 0; 420 } 421 422 /** */ 423 public void restarted() 424 { 425 soup_message_restarted(soupMessage); 426 } 427 428 /** 429 * Sets an alternate chunk-allocation function to use when reading 430 * @msg's body when using the traditional (ie, 431 * non-#SoupRequest<!-- -->-based) API. Every time data is available 432 * to read, libsoup will call @allocator, which should return a 433 * #SoupBuffer. (See #SoupChunkAllocator for additional details.) 434 * Libsoup will then read data from the network into that buffer, and 435 * update the buffer's <literal>length</literal> to indicate how much 436 * data it read. 437 * 438 * Generally, a custom chunk allocator would be used in conjunction 439 * with soup_message_body_set_accumulate() %FALSE and 440 * #SoupMessage::got_chunk, as part of a strategy to avoid unnecessary 441 * copying of data. However, you cannot assume that every call to the 442 * allocator will be followed by a call to your 443 * #SoupMessage::got_chunk handler; if an I/O error occurs, then the 444 * buffer will be unreffed without ever having been used. If your 445 * buffer-allocation strategy requires special cleanup, use 446 * soup_buffer_new_with_owner() rather than doing the cleanup from the 447 * #SoupMessage::got_chunk handler. 448 * 449 * The other thing to remember when using non-accumulating message 450 * bodies is that the buffer passed to the #SoupMessage::got_chunk 451 * handler will be unreffed after the handler returns, just as it 452 * would be in the non-custom-allocated case. If you want to hand the 453 * chunk data off to some other part of your program to use later, 454 * you'll need to ref the #SoupBuffer (or its owner, in the 455 * soup_buffer_new_with_owner() case) to ensure that the data remains 456 * valid. 457 * 458 * Deprecated: #SoupRequest provides a much simpler API that lets you 459 * read the response directly into your own buffers without needing to 460 * mess with callbacks, pausing/unpausing, etc. 461 * 462 * Params: 463 * allocator = the chunk allocator callback 464 * userData = data to pass to @allocator 465 * destroyNotify = destroy notifier to free @user_data when @msg is 466 * destroyed 467 */ 468 public void setChunkAllocator(SoupChunkAllocator allocator, void* userData, GDestroyNotify destroyNotify) 469 { 470 soup_message_set_chunk_allocator(soupMessage, allocator, userData, destroyNotify); 471 } 472 473 /** 474 * Sets @first_party as the main document #SoupURI for @msg. For 475 * details of when and how this is used refer to the documentation for 476 * #SoupCookieJarAcceptPolicy. 477 * 478 * Params: 479 * firstParty = the #SoupURI for the @msg's first party 480 * 481 * Since: 2.30 482 */ 483 public void setFirstParty(URI firstParty) 484 { 485 soup_message_set_first_party(soupMessage, (firstParty is null) ? null : firstParty.getURIStruct()); 486 } 487 488 /** 489 * Sets the specified flags on @msg. 490 * 491 * Params: 492 * flags = a set of #SoupMessageFlags values 493 */ 494 public void setFlags(SoupMessageFlags flags) 495 { 496 soup_message_set_flags(soupMessage, flags); 497 } 498 499 /** 500 * Sets the HTTP version on @msg. The default version is 501 * %SOUP_HTTP_1_1. Setting it to %SOUP_HTTP_1_0 will prevent certain 502 * functionality from being used. 503 * 504 * Params: 505 * version_ = the HTTP version 506 */ 507 public void setHttpVersion(SoupHTTPVersion version_) 508 { 509 soup_message_set_http_version(soupMessage, version_); 510 } 511 512 /** 513 * See the [same-site spec](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00) 514 * for more information. 515 * 516 * Params: 517 * isTopLevelNavigation = if %TRUE indicate the current request is a top-level navigation 518 * 519 * Since: 2.70 520 */ 521 public void setIsTopLevelNavigation(bool isTopLevelNavigation) 522 { 523 soup_message_set_is_top_level_navigation(soupMessage, isTopLevelNavigation); 524 } 525 526 /** 527 * Sets the priority of a message. Note that this won't have any 528 * effect unless used before the message is added to the session's 529 * message processing queue. 530 * 531 * The message will be placed just before any other previously added 532 * message with lower priority (messages with the same priority are 533 * processed on a FIFO basis). 534 * 535 * Setting priorities does not currently work with #SoupSessionSync 536 * (or with synchronous messages on a plain #SoupSession) because in 537 * the synchronous/blocking case, priority ends up being determined 538 * semi-randomly by thread scheduling. 539 * 540 * Params: 541 * priority = the #SoupMessagePriority 542 * 543 * Since: 2.44 544 */ 545 public void setPriority(SoupMessagePriority priority) 546 { 547 soup_message_set_priority(soupMessage, priority); 548 } 549 550 /** 551 * Sets @msg's status_code to @status_code and adds a Location header 552 * pointing to @redirect_uri. Use this from a #SoupServer when you 553 * want to redirect the client to another URI. 554 * 555 * @redirect_uri can be a relative URI, in which case it is 556 * interpreted relative to @msg's current URI. In particular, if 557 * @redirect_uri is just a path, it will replace the path 558 * <emphasis>and query</emphasis> of @msg's URI. 559 * 560 * Params: 561 * statusCode = a 3xx status code 562 * redirectUri = the URI to redirect @msg to 563 * 564 * Since: 2.38 565 */ 566 public void setRedirect(uint statusCode, string redirectUri) 567 { 568 soup_message_set_redirect(soupMessage, statusCode, Str.toStringz(redirectUri)); 569 } 570 571 /** 572 * Convenience function to set the request body of a #SoupMessage. If 573 * @content_type is %NULL, the request body must be empty as well. 574 * 575 * Params: 576 * contentType = MIME Content-Type of the body 577 * reqUse = a #SoupMemoryUse describing how to handle @req_body 578 * reqBody = a data buffer containing the body of the message request. 579 */ 580 public void setRequest(string contentType, SoupMemoryUse reqUse, string reqBody) 581 { 582 soup_message_set_request(soupMessage, Str.toStringz(contentType), reqUse, Str.toStringz(reqBody), cast(size_t)reqBody.length); 583 } 584 585 /** 586 * Convenience function to set the response body of a #SoupMessage. If 587 * @content_type is %NULL, the response body must be empty as well. 588 * 589 * Params: 590 * contentType = MIME Content-Type of the body 591 * respUse = a #SoupMemoryUse describing how to handle @resp_body 592 * respBody = a data buffer containing the body of the message response. 593 */ 594 public void setResponse(string contentType, SoupMemoryUse respUse, string respBody) 595 { 596 soup_message_set_response(soupMessage, Str.toStringz(contentType), respUse, Str.toStringz(respBody), cast(size_t)respBody.length); 597 } 598 599 /** 600 * Sets @site_for_cookies as the policy URL for same-site cookies for @msg. 601 * 602 * It is either the URL of the top-level document or %NULL depending on whether the registrable 603 * domain of this document's URL matches the registrable domain of its parent's/opener's 604 * URL. For the top-level document it is set to the document's URL. 605 * 606 * See the [same-site spec](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00) 607 * for more information. 608 * 609 * Params: 610 * siteForCookies = the #SoupURI for the @msg's site for cookies 611 * 612 * Since: 2.70 613 */ 614 public void setSiteForCookies(URI siteForCookies) 615 { 616 soup_message_set_site_for_cookies(soupMessage, (siteForCookies is null) ? null : siteForCookies.getURIStruct()); 617 } 618 619 /** 620 * Sets @msg's status code to @status_code. If @status_code is a 621 * known value, it will also set @msg's reason_phrase. 622 * 623 * Params: 624 * statusCode = an HTTP status code 625 */ 626 public void setStatus(uint statusCode) 627 { 628 soup_message_set_status(soupMessage, statusCode); 629 } 630 631 /** 632 * Sets @msg's status code and reason phrase. 633 * 634 * Params: 635 * statusCode = an HTTP status code 636 * reasonPhrase = a description of the status 637 */ 638 public void setStatusFull(uint statusCode, string reasonPhrase) 639 { 640 soup_message_set_status_full(soupMessage, statusCode, Str.toStringz(reasonPhrase)); 641 } 642 643 /** 644 * Sets @msg's URI to @uri. If @msg has already been sent and you want 645 * to re-send it with the new URI, you need to call 646 * soup_session_requeue_message(). 647 * 648 * Params: 649 * uri = the new #SoupURI 650 */ 651 public void setUri(URI uri) 652 { 653 soup_message_set_uri(soupMessage, (uri is null) ? null : uri.getURIStruct()); 654 } 655 656 /** */ 657 public void starting() 658 { 659 soup_message_starting(soupMessage); 660 } 661 662 /** */ 663 public void wroteBody() 664 { 665 soup_message_wrote_body(soupMessage); 666 } 667 668 /** */ 669 public void wroteBodyData(Buffer chunk) 670 { 671 soup_message_wrote_body_data(soupMessage, (chunk is null) ? null : chunk.getBufferStruct()); 672 } 673 674 /** */ 675 public void wroteChunk() 676 { 677 soup_message_wrote_chunk(soupMessage); 678 } 679 680 /** */ 681 public void wroteHeaders() 682 { 683 soup_message_wrote_headers(soupMessage); 684 } 685 686 /** */ 687 public void wroteInformational() 688 { 689 soup_message_wrote_informational(soupMessage); 690 } 691 692 /** 693 * This signal is emitted after #SoupMessage::got-headers, and 694 * before the first #SoupMessage::got-chunk. If content 695 * sniffing is disabled, or no content sniffing will be 696 * performed, due to the sniffer deciding to trust the 697 * Content-Type sent by the server, this signal is emitted 698 * immediately after #SoupMessage::got-headers, and @type is 699 * %NULL. 700 * 701 * If the #SoupContentSniffer feature is enabled, and the 702 * sniffer decided to perform sniffing, the first 703 * #SoupMessage::got-chunk emission may be delayed, so that the 704 * sniffer has enough data to correctly sniff the content. It 705 * notified the library user that the content has been 706 * sniffed, and allows it to change the header contents in the 707 * message, if desired. 708 * 709 * After this signal is emitted, the data that was spooled so 710 * that sniffing could be done is delivered on the first 711 * emission of #SoupMessage::got-chunk. 712 * 713 * Params: 714 * type = the content type that we got from sniffing 715 * params = a #GHashTable with the parameters 716 * 717 * Since: 2.28 718 */ 719 gulong addOnContentSniffed(void delegate(string, HashTable, Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 720 { 721 return Signals.connect(this, "content-sniffed", dlg, connectFlags ^ ConnectFlags.SWAPPED); 722 } 723 724 /** 725 * Emitted when all HTTP processing is finished for a message. 726 * (After #SoupMessage::got_body for client-side messages, or 727 * after #SoupMessage::wrote_body for server-side messages.) 728 */ 729 gulong addOnFinished(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 730 { 731 return Signals.connect(this, "finished", dlg, connectFlags ^ ConnectFlags.SWAPPED); 732 } 733 734 /** 735 * Emitted after receiving the complete message body. (For a 736 * server-side message, this means it has received the request 737 * body. For a client-side message, this means it has received 738 * the response body and is nearly done with the message.) 739 * 740 * See also soup_message_add_header_handler() and 741 * soup_message_add_status_code_handler(), which can be used 742 * to connect to a subset of emissions of this signal. 743 */ 744 gulong addOnGotBody(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 745 { 746 return Signals.connect(this, "got-body", dlg, connectFlags ^ ConnectFlags.SWAPPED); 747 } 748 749 /** 750 * Emitted after receiving a chunk of a message body. Note 751 * that "chunk" in this context means any subpiece of the 752 * body, not necessarily the specific HTTP 1.1 chunks sent by 753 * the other side. 754 * 755 * If you cancel or requeue @msg while processing this signal, 756 * then the current HTTP I/O will be stopped after this signal 757 * emission finished, and @msg's connection will be closed. 758 * 759 * Params: 760 * chunk = the just-read chunk 761 */ 762 gulong addOnGotChunk(void delegate(Buffer, Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 763 { 764 return Signals.connect(this, "got-chunk", dlg, connectFlags ^ ConnectFlags.SWAPPED); 765 } 766 767 /** 768 * Emitted after receiving all message headers for a message. 769 * (For a client-side message, this is after receiving the 770 * Status-Line and response headers; for a server-side 771 * message, it is after receiving the Request-Line and request 772 * headers.) 773 * 774 * See also soup_message_add_header_handler() and 775 * soup_message_add_status_code_handler(), which can be used 776 * to connect to a subset of emissions of this signal. 777 * 778 * If you cancel or requeue @msg while processing this signal, 779 * then the current HTTP I/O will be stopped after this signal 780 * emission finished, and @msg's connection will be closed. 781 * (If you need to requeue a message--eg, after handling 782 * authentication or redirection--it is usually better to 783 * requeue it from a #SoupMessage::got_body handler rather 784 * than a #SoupMessage::got_headers handler, so that the 785 * existing HTTP connection can be reused.) 786 */ 787 gulong addOnGotHeaders(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 788 { 789 return Signals.connect(this, "got-headers", dlg, connectFlags ^ ConnectFlags.SWAPPED); 790 } 791 792 /** 793 * Emitted after receiving a 1xx (Informational) response for 794 * a (client-side) message. The response_headers will be 795 * filled in with the headers associated with the 796 * informational response; however, those header values will 797 * be erased after this signal is done. 798 * 799 * If you cancel or requeue @msg while processing this signal, 800 * then the current HTTP I/O will be stopped after this signal 801 * emission finished, and @msg's connection will be closed. 802 */ 803 gulong addOnGotInformational(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 804 { 805 return Signals.connect(this, "got-informational", dlg, connectFlags ^ ConnectFlags.SWAPPED); 806 } 807 808 /** 809 * Emitted to indicate that some network-related event 810 * related to @msg has occurred. This essentially proxies the 811 * #GSocketClient::event signal, but only for events that 812 * occur while @msg "owns" the connection; if @msg is sent on 813 * an existing persistent connection, then this signal will 814 * not be emitted. (If you want to force the message to be 815 * sent on a new connection, set the 816 * %SOUP_MESSAGE_NEW_CONNECTION flag on it.) 817 * 818 * See #GSocketClient::event for more information on what 819 * the different values of @event correspond to, and what 820 * @connection will be in each case. 821 * 822 * Params: 823 * event = the network event 824 * connection = the current state of the network connection 825 * 826 * Since: 2.38 827 */ 828 gulong addOnNetwork(void delegate(GSocketClientEvent, IOStream, Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 829 { 830 return Signals.connect(this, "network-event", dlg, connectFlags ^ ConnectFlags.SWAPPED); 831 } 832 833 /** 834 * Emitted when a request that was already sent once is now 835 * being sent again (eg, because the first attempt received a 836 * redirection response, or because we needed to use 837 * authentication). 838 */ 839 gulong addOnRestarted(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 840 { 841 return Signals.connect(this, "restarted", dlg, connectFlags ^ ConnectFlags.SWAPPED); 842 } 843 844 /** 845 * Emitted just before a message is sent. 846 * 847 * Since: 2.50 848 */ 849 gulong addOnStarting(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 850 { 851 return Signals.connect(this, "starting", dlg, connectFlags ^ ConnectFlags.SWAPPED); 852 } 853 854 /** 855 * Emitted immediately after writing the complete body for a 856 * message. (For a client-side message, this means that 857 * libsoup is done writing and is now waiting for the response 858 * from the server. For a server-side message, this means that 859 * libsoup has finished writing the response and is nearly 860 * done with the message.) 861 */ 862 gulong addOnWroteBody(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 863 { 864 return Signals.connect(this, "wrote-body", dlg, connectFlags ^ ConnectFlags.SWAPPED); 865 } 866 867 /** 868 * Emitted immediately after writing a portion of the message 869 * body to the network. 870 * 871 * Unlike #SoupMessage::wrote_chunk, this is emitted after 872 * every successful write() call, not only after finishing a 873 * complete "chunk". 874 * 875 * Params: 876 * chunk = the data written 877 * 878 * Since: 2.24 879 */ 880 gulong addOnWroteBodyData(void delegate(Buffer, Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 881 { 882 return Signals.connect(this, "wrote-body-data", dlg, connectFlags ^ ConnectFlags.SWAPPED); 883 } 884 885 /** 886 * Emitted immediately after writing a body chunk for a message. 887 * 888 * Note that this signal is not parallel to 889 * #SoupMessage::got_chunk; it is emitted only when a complete 890 * chunk (added with soup_message_body_append() or 891 * soup_message_body_append_buffer()) has been written. To get 892 * more useful continuous progress information, use 893 * #SoupMessage::wrote_body_data. 894 */ 895 gulong addOnWroteChunk(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 896 { 897 return Signals.connect(this, "wrote-chunk", dlg, connectFlags ^ ConnectFlags.SWAPPED); 898 } 899 900 /** 901 * Emitted immediately after writing the headers for a 902 * message. (For a client-side message, this is after writing 903 * the request headers; for a server-side message, it is after 904 * writing the response headers.) 905 */ 906 gulong addOnWroteHeaders(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 907 { 908 return Signals.connect(this, "wrote-headers", dlg, connectFlags ^ ConnectFlags.SWAPPED); 909 } 910 911 /** 912 * Emitted immediately after writing a 1xx (Informational) 913 * response for a (server-side) message. 914 */ 915 gulong addOnWroteInformational(void delegate(Message) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0) 916 { 917 return Signals.connect(this, "wrote-informational", dlg, connectFlags ^ ConnectFlags.SWAPPED); 918 } 919 }