1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
use crate::{prelude::*, Server, ServerMessage, WebsocketConnection};
use glib::translate::*;
use std::boxed::Box as Box_;
use std::collections::HashMap;

mod sealed {
    pub trait Sealed {}
    impl<T: super::IsA<crate::Server>> Sealed for T {}
}

pub trait ServerExtManual: IsA<Server> + sealed::Sealed + 'static {
    /// Adds an "early" handler to @self for requests prefixed by @path.
    ///
    /// Note that "normal" and "early" handlers are matched up together, so if you
    /// add a normal handler for "/foo" and an early handler for "/foo/bar", then a
    /// request to "/foo/bar" (or any path below it) will run only the early handler.
    /// (But if you add both handlers at the same path, then both will get run.)
    ///
    /// For requests under @path (that have not already been assigned a
    /// status code by a [`AuthDomain`][crate::AuthDomain] or a signal handler), @callback
    /// will be invoked after receiving the request headers, but before
    /// receiving the request body; the message's method and
    /// request-headers properties will be set.
    ///
    /// Early handlers are generally used for processing requests with request bodies
    /// in a streaming fashion. If you determine that the request will contain a
    /// message body, normally you would call [`MessageBody::set_accumulate()`][crate::MessageBody::set_accumulate()] on
    /// the message's request-body to turn off request-body accumulation, and connect
    /// to the message's [`got-chunk`][struct@crate::ServerMessage#got-chunk] signal to process each
    /// chunk as it comes in.
    ///
    /// To complete the message processing after the full message body has
    /// been read, you can either also connect to [`got-body`][struct@crate::ServerMessage#got-body],
    /// or else you can register a non-early handler for @path as well. As
    /// long as you have not set the status-code by the time
    /// [`got-body`][struct@crate::ServerMessage#got-body] is emitted, the non-early handler will be
    /// run as well.
    /// ## `path`
    /// the toplevel path for the handler
    /// ## `callback`
    /// callback to invoke for
    ///   requests under @path
    #[doc(alias = "soup_server_add_early_handler")]
    fn add_early_handler<P: Fn(&Server, &ServerMessage, &str, HashMap<&str, &str>) + 'static>(
        &self,
        path: Option<&str>,
        callback: P,
    ) {
        let callback_data: Box_<P> = Box_::new(callback);
        unsafe extern "C" fn callback_func<
            P: Fn(&Server, &ServerMessage, &str, HashMap<&str, &str>) + 'static,
        >(
            server: *mut ffi::SoupServer,
            msg: *mut ffi::SoupServerMessage,
            path: *const libc::c_char,
            query: *mut glib::ffi::GHashTable,
            user_data: glib::ffi::gpointer,
        ) {
            let server = from_glib_borrow(server);
            let msg: Borrowed<ServerMessage> = from_glib_borrow(msg);
            let path: Borrowed<glib::GString> = from_glib_borrow(path);
            let query_map = query_map_from_hash_table(query);
            let callback: &P = &*(user_data as *mut _);
            (*callback)(&server, &msg, path.as_str(), query_map);
        }
        let callback = Some(callback_func::<P> as _);
        unsafe extern "C" fn destroy_func<
            P: Fn(&Server, &ServerMessage, &str, HashMap<&str, &str>) + 'static,
        >(
            data: glib::ffi::gpointer,
        ) {
            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
        }
        let destroy_call6 = Some(destroy_func::<P> as _);
        let super_callback0: Box_<P> = callback_data;
        unsafe {
            ffi::soup_server_add_early_handler(
                self.as_ref().to_glib_none().0,
                path.to_glib_none().0,
                callback,
                Box_::into_raw(super_callback0) as *mut _,
                destroy_call6,
            );
        }
    }

    /// Adds a handler to @self for requests prefixed by @path.
    ///
    /// If @path is [`None`] or "/", then this will be the default handler for all
    /// requests that don't have a more specific handler. (Note though that if you
    /// want to handle requests to the special "*" URI, you must explicitly register
    /// a handler for "*"; the default handler will not be used for that case.)
    ///
    /// For requests under @path (that have not already been assigned a
    /// status code by a [`AuthDomain`][crate::AuthDomain], an early server handler, or a
    /// signal handler), @callback will be invoked after receiving the
    /// request body; the [`ServerMessage`][crate::ServerMessage]'s method, request-headers,
    /// and request-body properties will be set.
    ///
    /// After determining what to do with the request, the callback must at a minimum
    /// call [`ServerMessage::set_status()`][crate::ServerMessage::set_status()] on the message to set the response
    /// status code. Additionally, it may set response headers and/or fill in the
    /// response body.
    ///
    /// If the callback cannot fully fill in the response before returning
    /// (eg, if it needs to wait for information from a database, or
    /// another network server), it should call [`ServerMessage::pause()`][crate::ServerMessage::pause()]
    /// to tell @self to not send the response right away. When the
    /// response is ready, call [`ServerMessage::unpause()`][crate::ServerMessage::unpause()] to cause it
    /// to be sent.
    ///
    /// To send the response body a bit at a time using "chunked" encoding, first
    /// call [`MessageHeaders::set_encoding()`][crate::MessageHeaders::set_encoding()] to set [`Encoding::Chunked`][crate::Encoding::Chunked] on
    /// the response-headers. Then call `MessageBody::append()` (or
    /// [`MessageBody::append_bytes()`][crate::MessageBody::append_bytes()])) to append each chunk as it becomes ready,
    /// and [`ServerMessage::unpause()`][crate::ServerMessage::unpause()] to make sure it's running. (The server
    /// will automatically pause the message if it is using chunked encoding but no
    /// more chunks are available.) When you are done, call
    /// [`MessageBody::complete()`][crate::MessageBody::complete()] to indicate that no more chunks are coming.
    /// ## `path`
    /// the toplevel path for the handler
    /// ## `callback`
    /// callback to invoke for
    ///   requests under @path
    #[doc(alias = "soup_server_add_handler")]
    fn add_handler<P: Fn(&Server, &ServerMessage, &str, HashMap<&str, &str>) + 'static>(
        &self,
        path: Option<&str>,
        callback: P,
    ) {
        let callback_data: Box_<P> = Box_::new(callback);
        unsafe extern "C" fn callback_func<
            P: Fn(&Server, &ServerMessage, &str, HashMap<&str, &str>) + 'static,
        >(
            server: *mut ffi::SoupServer,
            msg: *mut ffi::SoupServerMessage,
            path: *const libc::c_char,
            query: *mut glib::ffi::GHashTable,
            user_data: glib::ffi::gpointer,
        ) {
            let server = from_glib_borrow(server);
            let msg: Borrowed<ServerMessage> = from_glib_borrow(msg);
            let path: Borrowed<glib::GString> = from_glib_borrow(path);
            let query_map = query_map_from_hash_table(query);
            let callback: &P = &*(user_data as *mut _);
            (*callback)(&server, &msg, path.as_str(), query_map);
        }
        let callback = Some(callback_func::<P> as _);
        unsafe extern "C" fn destroy_func<
            P: Fn(&Server, &ServerMessage, &str, HashMap<&str, &str>) + 'static,
        >(
            data: glib::ffi::gpointer,
        ) {
            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
        }
        let destroy_call6 = Some(destroy_func::<P> as _);
        let super_callback0: Box_<P> = callback_data;
        unsafe {
            ffi::soup_server_add_handler(
                self.as_ref().to_glib_none().0,
                path.to_glib_none().0,
                callback,
                Box_::into_raw(super_callback0) as *mut _,
                destroy_call6,
            );
        }
    }

    /// Adds a WebSocket handler to @self for requests prefixed by @path.
    ///
    /// If @path is [`None`] or "/", then this will be the default handler for all
    /// requests that don't have a more specific handler.
    ///
    /// When a path has a WebSocket handler registered, @self will check
    /// incoming requests for WebSocket handshakes after all other handlers
    /// have run (unless some earlier handler has already set a status code
    /// on the message), and update the request's status, response headers,
    /// and response body accordingly.
    ///
    /// If @origin is non-[`None`], then only requests containing a matching
    /// "Origin" header will be accepted. If @protocols is non-[`None`], then
    /// only requests containing a compatible "Sec-WebSocket-Protocols"
    /// header will be accepted. More complicated requirements can be
    /// handled by adding a normal handler to @path, and having it perform
    /// whatever checks are needed and
    /// setting a failure status code if the handshake should be rejected.
    /// ## `path`
    /// the toplevel path for the handler
    /// ## `origin`
    /// the origin of the connection
    /// ## `protocols`
    /// the protocols
    ///   supported by this handler
    /// ## `callback`
    /// callback to invoke for
    ///   successful WebSocket requests under @path
    #[doc(alias = "soup_server_add_websocket_handler")]
    fn add_websocket_handler<
        P: Fn(&Server, &ServerMessage, &str, &WebsocketConnection) + 'static,
    >(
        &self,
        path: Option<&str>,
        origin: Option<&str>,
        protocols: &[&str],
        callback: P,
    ) {
        let callback_data: Box_<P> = Box_::new(callback);
        unsafe extern "C" fn callback_func<
            P: Fn(&Server, &ServerMessage, &str, &WebsocketConnection) + 'static,
        >(
            server: *mut ffi::SoupServer,
            msg: *mut ffi::SoupServerMessage,
            path: *const libc::c_char,
            connection: *mut ffi::SoupWebsocketConnection,
            user_data: glib::ffi::gpointer,
        ) {
            let server = from_glib_borrow(server);
            let msg: Borrowed<ServerMessage> = from_glib_borrow(msg);
            let path: Borrowed<glib::GString> = from_glib_borrow(path);
            let connection = from_glib_borrow(connection);
            let callback: &P = &*(user_data as *mut _);
            (*callback)(&server, &msg, path.as_str(), &connection);
        }
        let callback = Some(callback_func::<P> as _);
        unsafe extern "C" fn destroy_func<
            P: Fn(&Server, &ServerMessage, &str, &WebsocketConnection) + 'static,
        >(
            data: glib::ffi::gpointer,
        ) {
            let _callback: Box_<P> = Box_::from_raw(data as *mut _);
        }
        let destroy_call6 = Some(destroy_func::<P> as _);
        let super_callback0: Box_<P> = callback_data;
        unsafe {
            ffi::soup_server_add_websocket_handler(
                self.as_ref().to_glib_none().0,
                path.to_glib_none().0,
                origin.to_glib_none().0,
                protocols.to_glib_none().0,
                callback,
                Box_::into_raw(super_callback0) as *mut _,
                destroy_call6,
            );
        }
    }
}

impl<O: IsA<Server>> ServerExtManual for O {}

unsafe fn query_map_from_hash_table<'a>(
    query: *mut glib::ffi::GHashTable,
) -> HashMap<&'a str, &'a str> {
    unsafe extern "C" fn read_query_hash_table(
        key: glib::ffi::gpointer,
        value: glib::ffi::gpointer,
        hash_map: glib::ffi::gpointer,
    ) {
        let key = glib::GStr::from_ptr_checked(key as *const libc::c_char);
        let value = glib::GStr::from_ptr_checked(value as *const libc::c_char);
        let hash_map: &mut HashMap<&str, &str> = &mut *(hash_map as *mut HashMap<&str, &str>);
        if let (Some(k), Some(v)) = (key, value) {
            hash_map.insert(k.as_str(), v.as_str());
        }
    }
    unsafe {
        let mut query_map = HashMap::with_capacity(glib::ffi::g_hash_table_size(query) as usize);
        glib::ffi::g_hash_table_foreach(
            query,
            Some(read_query_hash_table),
            &mut query_map as *mut HashMap<&str, &str> as *mut _,
        );
        query_map
    }
}