1 /*
2 * soup-server-message.c: HTTP server request/response
3 *
4 * Copyright (C) 2020 Igalia S.L.
5 */
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <string.h>
12
13 #include "soup-server-message.h"
14 #include "soup.h"
15 #include "soup-connection.h"
16 #include "soup-server-message-private.h"
17 #include "soup-message-headers-private.h"
18 #include "soup-socket.h"
19 #include "soup-uri-utils-private.h"
20
21 /**
22 * SECTION:soup-server-message
23 * @short_description: An HTTP server request and response.
24 * @see_also: #SoupMessageHeaders, #SoupMessageBody
25 *
26 * A SoupServerMessage represents an HTTP message that is being sent or
27 * received on a #SoupServer
28 *
29 * #SoupServer will create #SoupServerMessage<!-- -->s automatically for
30 * incoming requests, which your application will receive via handlers.
31 *
32 * Note that libsoup's terminology here does not quite match the HTTP
33 * specification: in RFC 2616, an "HTTP-message" is
34 * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a
35 * Response. In libsoup, a #SoupServerMessage combines both the request and
36 * the response.
37 **/
38
39 /**
40 * SoupServerMessage:
41 *
42 * Class represnting an HTTP request and response pair for a server.
43 */
44
45 struct _SoupServerMessage {
46 GObject parent;
47
48 SoupSocket *sock;
49 GSocket *gsock;
50 SoupAuthDomain *auth_domain;
51 char *auth_user;
52
53 GSocketAddress *remote_addr;
54 char *remote_ip;
55 GSocketAddress *local_addr;
56
57 const char *method;
58 SoupHTTPVersion http_version;
59 SoupHTTPVersion orig_http_version;
60
61 guint status_code;
62 char *reason_phrase;
63
64 GUri *uri;
65
66 SoupMessageBody *request_body;
67 SoupMessageHeaders *request_headers;
68
69 SoupMessageBody *response_body;
70 SoupMessageHeaders *response_headers;
71
72 SoupServerMessageIOData *io_data;
73
74 gboolean options_ping;
75 };
76
77 struct _SoupServerMessageClass {
78 GObjectClass parent_class;
79 };
80
81 G_DEFINE_FINAL_TYPE (SoupServerMessage, soup_server_message, G_TYPE_OBJECT)
82
83 enum {
84 WROTE_INFORMATIONAL,
85 WROTE_HEADERS,
86 WROTE_CHUNK,
87 WROTE_BODY_DATA,
88 WROTE_BODY,
89
90 GOT_HEADERS,
91 GOT_CHUNK,
92 GOT_BODY,
93
94 DISCONNECTED,
95 FINISHED,
96
97 ACCEPT_CERTIFICATE,
98
99 LAST_SIGNAL
100 };
101
102 static guint signals[LAST_SIGNAL] = { 0 };
103
104 static void
soup_server_message_init(SoupServerMessage * msg)105 soup_server_message_init (SoupServerMessage *msg)
106 {
107 msg->request_body = soup_message_body_new ();
108 msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
109 msg->response_body = soup_message_body_new ();
110 msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
111 soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CONTENT_LENGTH);
112 }
113
114 static void
soup_server_message_finalize(GObject * object)115 soup_server_message_finalize (GObject *object)
116 {
117 SoupServerMessage *msg = SOUP_SERVER_MESSAGE (object);
118
119 soup_server_message_io_data_free (msg->io_data);
120
121 g_clear_object (&msg->auth_domain);
122 g_clear_pointer (&msg->auth_user, g_free);
123 g_clear_object (&msg->remote_addr);
124 g_clear_object (&msg->local_addr);
125
126 if (msg->sock) {
127 g_signal_handlers_disconnect_by_data (msg->sock, msg);
128 g_object_unref (msg->sock);
129 }
130 g_clear_object (&msg->gsock);
131 g_clear_pointer (&msg->remote_ip, g_free);
132
133 g_clear_pointer (&msg->uri, g_uri_unref);
134 g_free (msg->reason_phrase);
135
136 soup_message_body_unref (msg->request_body);
137 soup_message_headers_unref (msg->request_headers);
138 soup_message_body_unref (msg->response_body);
139 soup_message_headers_unref (msg->response_headers);
140
141 G_OBJECT_CLASS (soup_server_message_parent_class)->finalize (object);
142 }
143
144 static void
soup_server_message_class_init(SoupServerMessageClass * klass)145 soup_server_message_class_init (SoupServerMessageClass *klass)
146 {
147 GObjectClass *object_class = G_OBJECT_CLASS (klass);
148
149 object_class->finalize = soup_server_message_finalize;
150
151 /**
152 * SoupServerMessage::wrote-informational:
153 * @msg: the message
154 *
155 * Emitted immediately after writing a 1xx (Informational) response.
156 */
157 signals[WROTE_INFORMATIONAL] =
158 g_signal_new ("wrote-informational",
159 G_OBJECT_CLASS_TYPE (object_class),
160 G_SIGNAL_RUN_LAST,
161 0,
162 NULL, NULL,
163 NULL,
164 G_TYPE_NONE, 0);
165
166 /**
167 * SoupServerMessage::wrote-headers:
168 * @msg: the message
169 *
170 * Emitted immediately after writing the response headers for a
171 * message.
172 */
173 signals[WROTE_HEADERS] =
174 g_signal_new ("wrote-headers",
175 G_OBJECT_CLASS_TYPE (object_class),
176 G_SIGNAL_RUN_LAST,
177 0,
178 NULL, NULL,
179 NULL,
180 G_TYPE_NONE, 0);
181
182 /**
183 * SoupServerMessage::wrote-chunk:
184 * @msg: the message
185 *
186 * Emitted immediately after writing a body chunk for a message.
187 *
188 * Note that this signal is not parallel to
189 * #SoupServerMessage::got-chunk; it is emitted only when a complete
190 * chunk (added with soup_message_body_append() or
191 * soup_message_body_append_bytes()) has been written. To get
192 * more useful continuous progress information, use
193 * #SoupServerMessage::wrote-body-data.
194 */
195 signals[WROTE_CHUNK] =
196 g_signal_new ("wrote-chunk",
197 G_OBJECT_CLASS_TYPE (object_class),
198 G_SIGNAL_RUN_LAST,
199 0,
200 NULL, NULL,
201 NULL,
202 G_TYPE_NONE, 0);
203
204 /**
205 * SoupServerMessage::wrote-body-data:
206 * @msg: the message
207 * @chunk_size: the number of bytes written
208 *
209 * Emitted immediately after writing a portion of the message
210 * body to the network.
211 */
212 signals[WROTE_BODY_DATA] =
213 g_signal_new ("wrote-body-data",
214 G_OBJECT_CLASS_TYPE (object_class),
215 G_SIGNAL_RUN_LAST,
216 0,
217 NULL, NULL,
218 NULL,
219 G_TYPE_NONE, 1,
220 G_TYPE_UINT);
221
222 /**
223 * SoupServerMessage::wrote-body:
224 * @msg: the message
225 *
226 * Emitted immediately after writing the complete response body for a
227 * message.
228 */
229 signals[WROTE_BODY] =
230 g_signal_new ("wrote-body",
231 G_OBJECT_CLASS_TYPE (object_class),
232 G_SIGNAL_RUN_LAST,
233 0,
234 NULL, NULL,
235 NULL,
236 G_TYPE_NONE, 0);
237
238 /**
239 * SoupServerMessage::got-headers:
240 * @msg: the message
241 *
242 * Emitted after receiving the Request-Line and request headers.
243 */
244 signals[GOT_HEADERS] =
245 g_signal_new ("got-headers",
246 G_OBJECT_CLASS_TYPE (object_class),
247 G_SIGNAL_RUN_LAST,
248 0,
249 NULL, NULL,
250 NULL,
251 G_TYPE_NONE, 0);
252
253 /**
254 * SoupServerMessage::got-chunk:
255 * @msg: the message
256 * @chunk: the just-read chunk
257 *
258 * Emitted after receiving a chunk of a message body. Note
259 * that "chunk" in this context means any subpiece of the
260 * body, not necessarily the specific HTTP 1.1 chunks sent by
261 * the other side.
262 */
263 signals[GOT_CHUNK] =
264 g_signal_new ("got-chunk",
265 G_OBJECT_CLASS_TYPE (object_class),
266 G_SIGNAL_RUN_FIRST,
267 0,
268 NULL, NULL,
269 NULL,
270 G_TYPE_NONE, 1,
271 G_TYPE_BYTES);
272
273 /**
274 * SoupServerMessage::got-body:
275 * @msg: the message
276 *
277 * Emitted after receiving the complete request body.
278 */
279 signals[GOT_BODY] =
280 g_signal_new ("got-body",
281 G_OBJECT_CLASS_TYPE (object_class),
282 G_SIGNAL_RUN_LAST,
283 0,
284 NULL, NULL,
285 NULL,
286 G_TYPE_NONE, 0);
287
288 /**
289 * SoupServerMessage::finished:
290 * @msg: the message
291 *
292 * Emitted when all HTTP processing is finished for a message.
293 * (After #SoupServerMessage::wrote-body).
294 */
295 signals[FINISHED] =
296 g_signal_new ("finished",
297 G_OBJECT_CLASS_TYPE (object_class),
298 G_SIGNAL_RUN_LAST,
299 0,
300 NULL, NULL,
301 NULL,
302 G_TYPE_NONE, 0);
303
304 /**
305 * SoupServerMessage::disconnected:
306 * @msg: the message
307 *
308 * Emitted when the @msg's socket is disconnected.
309 */
310 signals[DISCONNECTED] =
311 g_signal_new ("disconnected",
312 G_OBJECT_CLASS_TYPE (object_class),
313 G_SIGNAL_RUN_LAST,
314 0,
315 NULL, NULL,
316 NULL,
317 G_TYPE_NONE, 0);
318
319 /**
320 * SoupServerMessage::accept-certificate:
321 * @msg: the message
322 * @tls_peer_certificate: the peer's #GTlsCertificate
323 * @tls_peer_errors: the tls errors of @tls_certificate
324 *
325 * Emitted during the @msg's connection TLS handshake
326 * after client TLS certificate has been received.
327 * You can return %TRUE to accept @tls_certificate despite
328 * @tls_errors.
329 *
330 * Returns: %TRUE to accept the TLS certificate and stop other
331 * handlers from being invoked, or %FALSE to propagate the
332 * event further.
333 */
334 signals[ACCEPT_CERTIFICATE] =
335 g_signal_new ("accept-certificate",
336 G_OBJECT_CLASS_TYPE (object_class),
337 G_SIGNAL_RUN_LAST,
338 0,
339 g_signal_accumulator_true_handled, NULL,
340 NULL,
341 G_TYPE_BOOLEAN, 2,
342 G_TYPE_TLS_CERTIFICATE,
343 G_TYPE_TLS_CERTIFICATE_FLAGS);
344 }
345
346 static void
socket_disconnected(SoupServerMessage * msg)347 socket_disconnected (SoupServerMessage *msg)
348 {
349 g_signal_emit (msg, signals[DISCONNECTED], 0);
350 }
351
352 static gboolean
socket_accept_certificate(SoupServerMessage * msg,GTlsCertificate * tls_certificate,GTlsCertificateFlags * tls_errors)353 socket_accept_certificate (SoupServerMessage *msg,
354 GTlsCertificate *tls_certificate,
355 GTlsCertificateFlags *tls_errors)
356 {
357 gboolean accept = FALSE;
358
359 g_signal_emit (msg, signals[ACCEPT_CERTIFICATE], 0,
360 tls_certificate, tls_errors, &accept);
361 return accept;
362 }
363
364 SoupServerMessage *
soup_server_message_new(SoupSocket * sock)365 soup_server_message_new (SoupSocket *sock)
366 {
367 SoupServerMessage *msg;
368
369 msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
370 msg->sock = g_object_ref (sock);
371 msg->gsock = soup_socket_get_gsocket (sock);
372 if (msg->gsock)
373 g_object_ref (msg->gsock);
374
375 g_signal_connect_object (sock, "disconnected",
376 G_CALLBACK (socket_disconnected),
377 msg, G_CONNECT_SWAPPED);
378 g_signal_connect_object (sock, "accept-certificate",
379 G_CALLBACK (socket_accept_certificate),
380 msg, G_CONNECT_SWAPPED);
381
382 return msg;
383 }
384
385 void
soup_server_message_set_uri(SoupServerMessage * msg,GUri * uri)386 soup_server_message_set_uri (SoupServerMessage *msg,
387 GUri *uri)
388 {
389 if (msg->uri)
390 g_uri_unref (msg->uri);
391 msg->uri = soup_uri_copy_with_normalized_flags (uri);
392 }
393
394 SoupSocket *
soup_server_message_get_soup_socket(SoupServerMessage * msg)395 soup_server_message_get_soup_socket (SoupServerMessage *msg)
396 {
397 return msg->sock;
398 }
399
400 void
soup_server_message_set_auth(SoupServerMessage * msg,SoupAuthDomain * domain,char * user)401 soup_server_message_set_auth (SoupServerMessage *msg,
402 SoupAuthDomain *domain,
403 char *user)
404 {
405 if (msg->auth_domain)
406 g_object_unref (msg->auth_domain);
407 msg->auth_domain = domain;
408
409 if (msg->auth_user)
410 g_free (msg->auth_user);
411 msg->auth_user = user;
412 }
413
414 gboolean
soup_server_message_is_keepalive(SoupServerMessage * msg)415 soup_server_message_is_keepalive (SoupServerMessage *msg)
416 {
417 if (msg->status_code == SOUP_STATUS_OK && msg->method == SOUP_METHOD_CONNECT)
418 return TRUE;
419
420 /* Not persistent if the server sent a terminate-by-EOF response */
421 if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF)
422 return FALSE;
423
424 if (msg->http_version == SOUP_HTTP_1_0) {
425 /* In theory, HTTP/1.0 connections are only persistent
426 * if the client requests it, and the server agrees.
427 * But some servers do keep-alive even if the client
428 * doesn't request it. So ignore c_conn.
429 */
430
431 if (!soup_message_headers_header_contains_common (msg->response_headers,
432 SOUP_HEADER_CONNECTION,
433 "Keep-Alive"))
434 return FALSE;
435 } else {
436 /* Normally persistent unless either side requested otherwise */
437 if (soup_message_headers_header_contains_common (msg->request_headers,
438 SOUP_HEADER_CONNECTION,
439 "close") ||
440 soup_message_headers_header_contains_common (msg->response_headers,
441 SOUP_HEADER_CONNECTION,
442 "close"))
443 return FALSE;
444
445 return TRUE;
446 }
447
448 return TRUE;
449 }
450
451 void
soup_server_message_set_io_data(SoupServerMessage * msg,SoupServerMessageIOData * io)452 soup_server_message_set_io_data (SoupServerMessage *msg,
453 SoupServerMessageIOData *io)
454 {
455 soup_server_message_io_data_free (msg->io_data);
456 msg->io_data = io;
457 }
458
459 SoupServerMessageIOData *
soup_server_message_get_io_data(SoupServerMessage * msg)460 soup_server_message_get_io_data (SoupServerMessage *msg)
461 {
462 return msg->io_data;
463 }
464
465 void
soup_server_message_cleanup_response(SoupServerMessage * msg)466 soup_server_message_cleanup_response (SoupServerMessage *msg)
467 {
468 soup_message_body_truncate (msg->response_body);
469 soup_message_headers_clear (msg->response_headers);
470 soup_message_headers_set_encoding (msg->response_headers,
471 SOUP_ENCODING_CONTENT_LENGTH);
472 msg->status_code = SOUP_STATUS_NONE;
473 g_clear_pointer (&msg->reason_phrase, g_free);
474 msg->http_version = msg->orig_http_version;
475 }
476
477 void
soup_server_message_wrote_informational(SoupServerMessage * msg)478 soup_server_message_wrote_informational (SoupServerMessage *msg)
479 {
480 g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
481 }
482
483 void
soup_server_message_wrote_headers(SoupServerMessage * msg)484 soup_server_message_wrote_headers (SoupServerMessage *msg)
485 {
486 g_signal_emit (msg, signals[WROTE_HEADERS], 0);
487 }
488
489 void
soup_server_message_wrote_chunk(SoupServerMessage * msg)490 soup_server_message_wrote_chunk (SoupServerMessage *msg)
491 {
492 g_signal_emit (msg, signals[WROTE_CHUNK], 0);
493 }
494
495 void
soup_server_message_wrote_body_data(SoupServerMessage * msg,gsize chunk_size)496 soup_server_message_wrote_body_data (SoupServerMessage *msg,
497 gsize chunk_size)
498 {
499 g_signal_emit (msg, signals[WROTE_BODY_DATA], 0, chunk_size);
500 }
501
502 void
soup_server_message_wrote_body(SoupServerMessage * msg)503 soup_server_message_wrote_body (SoupServerMessage *msg)
504 {
505 g_signal_emit (msg, signals[WROTE_BODY], 0);
506 }
507
508 void
soup_server_message_got_headers(SoupServerMessage * msg)509 soup_server_message_got_headers (SoupServerMessage *msg)
510 {
511 g_signal_emit (msg, signals[GOT_HEADERS], 0);
512 }
513
514 void
soup_server_message_got_chunk(SoupServerMessage * msg,GBytes * chunk)515 soup_server_message_got_chunk (SoupServerMessage *msg,
516 GBytes *chunk)
517 {
518 g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk);
519 }
520
521 void
soup_server_message_got_body(SoupServerMessage * msg)522 soup_server_message_got_body (SoupServerMessage *msg)
523 {
524 if (soup_message_body_get_accumulate (msg->request_body))
525 g_bytes_unref (soup_message_body_flatten (msg->request_body));
526 g_signal_emit (msg, signals[GOT_BODY], 0);
527 }
528
529 void
soup_server_message_finished(SoupServerMessage * msg)530 soup_server_message_finished (SoupServerMessage *msg)
531 {
532 g_signal_emit (msg, signals[FINISHED], 0);
533 }
534
535 /**
536 * soup_server_message_get_request_headers:
537 * @msg: a #SoupServerMessage
538 *
539 * Get the request headers of @msg.
540 *
541 * Returns: (transfer none): a #SoupMessageHeaders with the request headers.
542 */
543 SoupMessageHeaders *
soup_server_message_get_request_headers(SoupServerMessage * msg)544 soup_server_message_get_request_headers (SoupServerMessage *msg)
545 {
546 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
547
548 return msg->request_headers;
549 }
550
551 /**
552 * soup_server_message_get_response_headers:
553 * @msg: a #SoupServerMessage
554 *
555 * Get the response headers of @msg.
556 *
557 * Returns: (transfer none): a #SoupMessageHeaders with the response headers.
558 */
559 SoupMessageHeaders *
soup_server_message_get_response_headers(SoupServerMessage * msg)560 soup_server_message_get_response_headers (SoupServerMessage *msg)
561 {
562 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
563
564 return msg->response_headers;
565 }
566
567 /**
568 * soup_server_message_get_request_body:
569 * @msg: a #SoupServerMessage
570 *
571 * Get the request body of @msg.
572 *
573 * Returns: (transfer none): a #SoupMessageBody.
574 */
575 SoupMessageBody *
soup_server_message_get_request_body(SoupServerMessage * msg)576 soup_server_message_get_request_body (SoupServerMessage *msg)
577 {
578 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
579
580 return msg->request_body;
581 }
582
583 /**
584 * soup_server_message_get_response_body:
585 * @msg: a #SoupServerMessage
586 *
587 * Get the response body of @msg.
588 *
589 * Returns: (transfer none): a #SoupMessageBody.
590 */
591 SoupMessageBody *
soup_server_message_get_response_body(SoupServerMessage * msg)592 soup_server_message_get_response_body (SoupServerMessage *msg)
593 {
594 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
595
596 return msg->response_body;
597 }
598
599 /**
600 * soup_server_message_get_method:
601 * @msg: a #SoupServerMessage
602 *
603 * Get the HTTP method of @msg.
604 *
605 * Returns: the HTTP method.
606 */
607 const char *
soup_server_message_get_method(SoupServerMessage * msg)608 soup_server_message_get_method (SoupServerMessage *msg)
609 {
610 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
611
612 return msg->method;
613 }
614
615 void
soup_server_message_set_method(SoupServerMessage * msg,const char * method)616 soup_server_message_set_method (SoupServerMessage *msg,
617 const char *method)
618 {
619 msg->method = g_intern_string (method);
620 }
621
622 void
soup_server_message_set_options_ping(SoupServerMessage * msg,gboolean is_options_ping)623 soup_server_message_set_options_ping (SoupServerMessage *msg,
624 gboolean is_options_ping)
625 {
626 msg->options_ping = is_options_ping;
627 }
628
629 /**
630 * soup_server_message_is_options_ping:
631 * @msg: a #SoupServerMessage
632 *
633 * Gets if @msg represents an OPTIONS message with the path `*`.
634 *
635 * Returns: %TRUE if is an OPTIONS ping
636 */
637 gboolean
soup_server_message_is_options_ping(SoupServerMessage * msg)638 soup_server_message_is_options_ping (SoupServerMessage *msg)
639 {
640 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), FALSE);
641
642 return msg->options_ping;
643 }
644
645 /**
646 * soup_server_message_get_http_version:
647 * @msg: a #SoupServerMessage
648 *
649 * Get the HTTP version of @msg.
650 *
651 * Returns: a #SoupHTTPVersion.
652 */
653 SoupHTTPVersion
soup_server_message_get_http_version(SoupServerMessage * msg)654 soup_server_message_get_http_version (SoupServerMessage *msg)
655 {
656 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), SOUP_HTTP_1_1);
657
658 return msg->http_version;
659 }
660
661 /**
662 * soup_server_message_set_http_version:
663 * @msg: a #SoupServerMessage
664 * @version: a #SoupHTTPVersion
665 *
666 * Set the HTTP version of @msg.
667 */
668 void
soup_server_message_set_http_version(SoupServerMessage * msg,SoupHTTPVersion version)669 soup_server_message_set_http_version (SoupServerMessage *msg,
670 SoupHTTPVersion version)
671 {
672 g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
673
674 msg->http_version = version;
675 if (msg->status_code == SOUP_STATUS_NONE)
676 msg->orig_http_version = version;
677 }
678
679 /**
680 * soup_server_message_get_reason_phrase:
681 * @msg: a #SoupServerMessage:
682 *
683 * Get the HTTP reason phrase of @msg or %NULL.
684 *
685 * Returns: the reason phrase.
686 */
687 const char *
soup_server_message_get_reason_phrase(SoupServerMessage * msg)688 soup_server_message_get_reason_phrase (SoupServerMessage *msg)
689 {
690 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
691
692 return msg->reason_phrase;
693 }
694
695 /**
696 * soup_server_message_get_status:
697 * @msg: a #SoupServerMessage
698 *
699 * Get the HTTP status code of @msg.
700 *
701 * Returns: the HTTP status code.
702 */
703 guint
soup_server_message_get_status(SoupServerMessage * msg)704 soup_server_message_get_status (SoupServerMessage *msg)
705 {
706 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), 0);
707
708 return msg->status_code;
709 }
710
711 /**
712 * soup_server_message_set_status:
713 * @msg: a #SoupServerMessage
714 * @status_code: an HTTP status code
715 * @reason_phrase: (nullable): a reason phrase
716 *
717 * Sets @msg's status code to @status_code. If @status_code is a
718 * known value and @reason_phrase is %NULL, the reason_phrase will
719 * be set automatically.
720 **/
721 void
soup_server_message_set_status(SoupServerMessage * msg,guint status_code,const char * reason_phrase)722 soup_server_message_set_status (SoupServerMessage *msg,
723 guint status_code,
724 const char *reason_phrase)
725 {
726 g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
727 g_return_if_fail (status_code != 0);
728
729 g_free (msg->reason_phrase);
730
731 msg->status_code = status_code;
732 msg->reason_phrase = g_strdup (reason_phrase ? reason_phrase : soup_status_get_phrase (status_code));
733 }
734
735 /**
736 * soup_server_message_get_uri:
737 * @msg: a #SoupServerMessage
738 *
739 * Get @msg's URI.
740 *
741 * Returns: (transfer none): a #GUri
742 */
743 GUri *
soup_server_message_get_uri(SoupServerMessage * msg)744 soup_server_message_get_uri (SoupServerMessage *msg)
745 {
746 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
747
748 return msg->uri;
749 }
750
751 /**
752 * soup_server_message_set_response:
753 * @msg: the message
754 * @content_type: (nullable): MIME Content-Type of the body
755 * @resp_use: a #SoupMemoryUse describing how to handle @resp_body
756 * @resp_body: (nullable) (array length=resp_length) (element-type guint8):
757 * a data buffer containing the body of the message response.
758 * @resp_length: the byte length of @resp_body.
759 *
760 * Convenience function to set the response body of a #SoupServerMessage. If
761 * @content_type is %NULL, the response body must be empty as well.
762 */
763 void
soup_server_message_set_response(SoupServerMessage * msg,const char * content_type,SoupMemoryUse resp_use,const char * resp_body,gsize resp_length)764 soup_server_message_set_response (SoupServerMessage *msg,
765 const char *content_type,
766 SoupMemoryUse resp_use,
767 const char *resp_body,
768 gsize resp_length)
769 {
770 g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
771 g_return_if_fail (content_type != NULL || resp_length == 0);
772
773 if (content_type) {
774 g_warn_if_fail (strchr (content_type, '/') != NULL);
775
776 soup_message_headers_replace_common (msg->response_headers,
777 SOUP_HEADER_CONTENT_TYPE,
778 content_type);
779 soup_message_body_append (msg->response_body, resp_use,
780 resp_body, resp_length);
781 } else {
782 soup_message_headers_remove_common (msg->response_headers,
783 SOUP_HEADER_CONTENT_TYPE);
784 soup_message_body_truncate (msg->response_body);
785 }
786 }
787
788 /**
789 * soup_server_message_set_redirect:
790 * @msg: a #SoupServerMessage
791 * @status_code: a 3xx status code
792 * @redirect_uri: the URI to redirect @msg to
793 *
794 * Sets @msg's status_code to @status_code and adds a Location header
795 * pointing to @redirect_uri. Use this from a #SoupServer when you
796 * want to redirect the client to another URI.
797 *
798 * @redirect_uri can be a relative URI, in which case it is
799 * interpreted relative to @msg's current URI. In particular, if
800 * @redirect_uri is just a path, it will replace the path
801 * <emphasis>and query</emphasis> of @msg's URI.
802 */
803 void
soup_server_message_set_redirect(SoupServerMessage * msg,guint status_code,const char * redirect_uri)804 soup_server_message_set_redirect (SoupServerMessage *msg,
805 guint status_code,
806 const char *redirect_uri)
807 {
808 GUri *location;
809 char *location_str;
810
811 g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
812
813 location = g_uri_parse_relative (soup_server_message_get_uri (msg), redirect_uri, SOUP_HTTP_URI_FLAGS, NULL);
814 g_return_if_fail (location != NULL);
815
816 soup_server_message_set_status (msg, status_code, NULL);
817 location_str = g_uri_to_string (location);
818 soup_message_headers_replace_common (msg->response_headers, SOUP_HEADER_LOCATION,
819 location_str);
820 g_free (location_str);
821 g_uri_unref (location);
822 }
823
824 /**
825 * soup_server_message_get_socket:
826 * @msg: a #SoupServerMessage
827 *
828 * Retrieves the #GSocket that @msg is associated with.
829 *
830 * If you are using this method to observe when multiple requests are
831 * made on the same persistent HTTP connection (eg, as the ntlm-test
832 * test program does), you will need to pay attention to socket
833 * destruction as well (eg, by using weak references), so that you do
834 * not get fooled when the allocator reuses the memory address of a
835 * previously-destroyed socket to represent a new socket.
836 *
837 * Returns: (nullable) (transfer none): the #GSocket that @msg is
838 * associated with, %NULL if you used soup_server_accept_iostream().
839 */
840 GSocket *
soup_server_message_get_socket(SoupServerMessage * msg)841 soup_server_message_get_socket (SoupServerMessage *msg)
842 {
843 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
844
845 return msg->gsock;
846 }
847
848 /**
849 * soup_server_message_get_remote_address:
850 * @msg: a #SoupServerMessage
851 *
852 * Retrieves the #GSocketAddress associated with the remote end
853 * of a connection.
854 *
855 * Returns: (nullable) (transfer none): the #GSocketAddress
856 * associated with the remote end of a connection, it may be
857 * %NULL if you used soup_server_accept_iostream().
858 */
859 GSocketAddress *
soup_server_message_get_remote_address(SoupServerMessage * msg)860 soup_server_message_get_remote_address (SoupServerMessage *msg)
861 {
862 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
863
864 if (msg->remote_addr)
865 return msg->remote_addr;
866
867 msg->remote_addr = msg->gsock ?
868 g_socket_get_remote_address (msg->gsock, NULL) :
869 G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_remote_address (msg->sock)));
870
871 return msg->remote_addr;
872 }
873
874 /**
875 * soup_server_message_get_local_address:
876 * @msg: a #SoupServerMessage
877 *
878 * Retrieves the #GSocketAddress associated with the local end
879 * of a connection.
880 *
881 * Returns: (nullable) (transfer none): the #GSocketAddress
882 * associated with the local end of a connection, it may be
883 * %NULL if you used soup_server_accept_iostream().
884 */
885 GSocketAddress *
soup_server_message_get_local_address(SoupServerMessage * msg)886 soup_server_message_get_local_address (SoupServerMessage *msg)
887 {
888 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
889
890 if (msg->local_addr)
891 return msg->local_addr;
892
893 msg->local_addr = msg->gsock ?
894 g_socket_get_local_address (msg->gsock, NULL) :
895 G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_local_address (msg->sock)));
896
897 return msg->local_addr;
898 }
899
900 /**
901 * soup_server_message_get_remote_host:
902 * @msg: a #SoupServerMessage
903 *
904 * Retrieves the IP address associated with the remote end of a
905 * connection.
906 *
907 * Returns: (nullable): the IP address associated with the remote
908 * end of a connection, it may be %NULL if you used
909 * soup_server_accept_iostream().
910 */
911 const char *
soup_server_message_get_remote_host(SoupServerMessage * msg)912 soup_server_message_get_remote_host (SoupServerMessage *msg)
913 {
914 g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
915
916 if (msg->remote_ip)
917 return msg->remote_ip;
918
919 if (msg->gsock) {
920 GSocketAddress *addr = soup_server_message_get_remote_address (msg);
921 GInetAddress *iaddr;
922
923 if (!addr || !G_IS_INET_SOCKET_ADDRESS (addr))
924 return NULL;
925 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
926 msg->remote_ip = g_inet_address_to_string (iaddr);
927 } else {
928 GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_socket_get_remote_address (msg->sock));
929 GInetAddress *inet_addr = g_inet_socket_address_get_address (addr);
930 msg->remote_ip = g_inet_address_to_string (inet_addr);
931 }
932
933 return msg->remote_ip;
934 }
935
936 /**
937 * soup_server_message_steal_connection:
938 * @msg: a #SoupServerMessage
939 *
940 * "Steals" the HTTP connection associated with @msg from its
941 * #SoupServer. This happens immediately, regardless of the current
942 * state of the connection; if the response to @msg has not yet finished
943 * being sent, then it will be discarded; you can steal the connection from a
944 * #SoupServerMessage::wrote-informational or #SoupServerMessage::wrote-body signal
945 * handler if you need to wait for part or all of the response to be sent.
946 *
947 * Note that when calling this function from C, @msg will most
948 * likely be freed as a side effect.
949 *
950 * Returns: (transfer full): the #GIOStream formerly associated
951 * with @msg (or %NULL if @msg was no longer associated with a
952 * connection). No guarantees are made about what kind of #GIOStream
953 * is returned.
954 */
955 GIOStream *
soup_server_message_steal_connection(SoupServerMessage * msg)956 soup_server_message_steal_connection (SoupServerMessage *msg)
957 {
958 GIOStream *stream;
959
960 g_object_ref (msg);
961 stream = soup_server_message_io_steal (msg);
962 if (stream) {
963 g_object_set_data_full (G_OBJECT (stream), "GSocket",
964 soup_socket_steal_gsocket (msg->sock),
965 g_object_unref);
966 }
967
968 g_signal_handlers_disconnect_by_data (msg, msg->sock);
969
970 socket_disconnected (msg);
971 g_object_unref (msg);
972
973 return stream;
974 }
975