summaryrefslogtreecommitdiff
path: root/src/lib/ecore_con/efl_net_dialer_websocket.eo
blob: 4bd96a5b7fd7f23fd80d05e21b803ae28901b415 (plain)
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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
import eina_types;
import efl_net_http_types;

enum @beta Efl.Net.Dialer_Websocket_Streaming_Mode {
    [[How to map WebSocket to EFL I/O Interfaces.
    ]]
    disabled, [[@Efl.Io.Writer.write and @Efl.Io.Reader.read will fail by returning ENOSTR]]
    binary, [[@Efl.Io.Writer.write will result in @Efl.Net.Dialer_Websocket.binary_send]]
    text, [[@Efl.Io.Writer.write will result in @Efl.Net.Dialer_Websocket.text_send]]
}

enum @beta Efl.Net.Dialer_Websocket_Close_Reason {
    [[Registered reasons for the CLOSE (opcode=0x8).

      These are the well known reasons, with some ranges being defined
      using "_start" and "end" suffixes.

      See https://tools.ietf.org/html/rfc6455#section-7.4.1
    ]]
    normal = 1000, [[Indicates a normal closure, meaning that the purpose for which the connection was established has been fulfilled.]]
    going_away = 1001, [[Indicates that an endpoint is "going away", such as a server going down or a browser having navigated away from a page.]]
    protocol_error = 1002, [[Indicates that an endpoint is terminating the connection due to a protocol error.]]
    no_reason = 1005, [[Reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting a status code to indicate that no status code was actually present.]]
    abruptly = 1006, [[Reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint.  It is designated for use in applications expecting a status code to indicate that the connection was closed abnormally, e.g., without sending or receiving a Close control frame.]]
    unexpected_data = 1003, [[Indicates that an endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message).]]
    inconsistent_data = 1007, [[Indicates that an endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 data within a text message).]]
    policy_violation = 1008, [[Indicates that an endpoint is terminating the connection because it has received a message that violates its policy.  This is a generic status code that can be returned when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a need to hide specific details about the policy.]]
    too_big = 1009, [[Indicates that an endpoint is terminating the connection because it has received a message that is too big for it to process.]]
    missing_extension = 1010, [[Indicates that an endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the server didn't return them in the response message of the WebSocket handshake.  The list of extensions that are needed SHOULD appear in the reason part of the Close frame.  Note that this status code is not used by the server, because it can fail the WebSocket handshake instead.]]
    server_error = 1011, [[Indicates that a server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.]]
    iana_registry_start = 3000, [[IANA registry starts at 3000]]
    iana_registry_end = 3999, [[IANA registry ends at 3999]]
    private_start = 4000, [[Applications can use range 4000-4999]]
    private_end = 4999, [[Applications can use range 4000-4999]]
}

struct @beta Efl.Net.Dialer_Websocket_Closed_Reason {
    [[Close reason event payload.
    ]]
    reason: Efl.Net.Dialer_Websocket_Close_Reason; [[Closing reason]]
    message: string; [[Textual closing reason message]]
}

class @beta Efl.Net.Dialer_Websocket extends Efl.Loop_Consumer implements Efl.Net.Dialer {
    [[WebSocket Dialer (Client).

      The WebSocket Protocol (https://tools.ietf.org/html/rfc6455) is
      a message-based protocol over HTTP, this allows it to leverage
      on authentication, cookies, proxies and SSL/TLS.

      Although it uses the HTTP dialer, it's
      not a subclass and thus not all HTTP features are exposed as the
      WebSocket has strict requirements that must be respected.

      If the proxy is $NULL (default), then the system proxy will be
      used. On UNIX that's the environment variable $http_proxy (or
      '$all_proxy') is used if the given address doesn't match
      $no_proxy patterns. To disable the proxy use an empty string. If
      provided, the proxy must be one of the following protocols:

       - http://username:password\@proxyserver:port
       - http://username\@proxyserver:port
       - http://proxyserver:port
       - http://proxyserver  (default port 1080)
       - socks5://username:password\@proxyserver:port (SOCKSv5)
       - socks5h://username\@proxyserver:port (let socks server to resolve domain)
       - socks5://proxyserver:port
       - socks5://proxyserver (default port 1080)
       - socks4a://proxyserver:port (SOCKSv4 and let socks server to resolve domain)
       - socks4://proxyserver:port (SOCKSv4)
    ]]

    methods {
        ping {
            [[Sends a PING (opcode=0x9) to the server.

              The server should reply with a PONG, which will be
              emitted as "pong" event.
            ]]

            params {
                reason: string @optional; [[Reason]]
            }
        }

        text_send {
            [[Sends an UTF-8 TEXT (opcode=0x1) to the server.

              The message text will be delivered as a single
              entity to the remote peer.

              The text is copied into a local buffer. No references
              are kept after this method returns.
            ]]
            params {
                text: string; [[Text to send]]
            }
        }

        binary_send {
            [[Sends a binary blob (opcode=0x2) to the server.

              The slice describing the blob goes in a message which will be
              delivered as a single entity to the remote peer.

              The memory is copied into a local buffer, no references
              are kept after this method returns.
            ]]
            params {
                blob: const(Eina.Slice); [[Binary blob to send]]
            }
        }

        close_request {
            [[Requests (opcode=0x8) the server to terminate the connection.

              Unlike @Efl.Io.Closer.close, this won't abruptly close
              the connection, rather it'll queue a message requesting
              the server to gracefully close it.

              After this method is called you should consider the
              object in "closing" state. No more messages can be sent
              (@.text_send, @.binary_send and @.ping will fail).

              The object will be automatically closed with
              @Efl.Io.Closer.close once the server replies with his own
              close message, which will be reported as "closed,reason".
            ]]
            params {
                reason: Efl.Net.Dialer_Websocket_Close_Reason; [[Reason for closing]]
                message: string @optional; [[Additional closing message]]
            }
        }

        request_protocol_add {
            [[Adds a new WebSocket protocol to the request.

              This should be set before dialing.
            ]]
            params {
                protocol: string; [[WebSocket protocol]]
            }
        }

        request_protocols_get {
            [[Returns an iterator to the requested WebSocket protocols]]
            return: iterator<string> @move @no_unused; [[Iterator to protocols]]
        }

        request_protocols_clear {
            [[Clears all request protocols]]
        }

        response_protocols_get {
            [[Returns an iterator to the server-replied (response) WebSocket protocols it supports]]
            return: iterator<string> @move @no_unused; [[Iterator to server protocols]]
        }

        @property streaming_mode {
            [[Configures how to map streaming APIs to WebSocket.

              WebSocket is a message-based protocol with these send
              via @.text_send and @.binary_send and delivered via
              events such as "message,text" and "message,binary".

              However this class can operate in streaming mode,
              mapping each @Efl.Io.Writer.write to a @.binary_send if
              streaming_mode is set to
              @Efl.Net.Dialer_Websocket_Streaming_Mode.binary, of
              @.text_send if
              @Efl.Net.Dialer_Websocket_Streaming_Mode.text

              @Efl.Io.Reader.read may consume less then the whole
              received message, in this case the rest of the message
              is kept for the next read call. (Note this differs from
              SOCK_SEQPACKET + read(2)).

              By default, streaming is disabled
              (@Efl.Net.Dialer_Websocket_Streaming_Mode.disabled).
            ]]
            get { }
            set { }
            values {
                streaming_mode: Efl.Net.Dialer_Websocket_Streaming_Mode; [[Streaming mode]]
            }
        }

        @property user_agent {
            [[The User-Agent to specify.

              This should be set before dialing.
            ]]
            get { }
            set { }
            values {
                user_agent: string; [[User-agent]]
            }
        }

        @property authentication {
            [[HTTP authentication to use.

              This should be set before dialing.
            ]]
            get { }
            set { }
            values {
                username: string; [[HTTP authentication username]]
                password: string; [[HTTP authentication password]]
                method: Efl.Net.Http.Authentication_Method @optional; [[The authentication method to use. Defaults to @Efl.Net.Http.Authentication_Method.basic]]
                restricted: bool @optional; [[Restrict method]]
            }
        }

        @property allow_redirects {
            [[Allows HTTP redirects to be followed.

              This should be set before dialing.
            ]]
            get { }
            set { }
            values {
                allow_redirects: bool; [[$true when following redirects, $false otherwise]]
            }
        }

        request_header_add {
            [[Adds a HTTP request header 'key: value'.

              See @.request_headers_clear

              WebSocket won't allow the following headers to be added
              as they conflict with its own operation:

                 - Content-Length
                 - Content-Type
                 - Transfer-Encoding
                 - Connection
                 - Upgrade
                 - Expect
                 - Sec-WebSocket-Version
                 - Sec-WebSocket-Key

              This should be called before dialing.
            ]]
            params {
                @in key: string; [[HTTP request header key]]
                @in value: string; [[HTTP request header value]]
            }
        }

        request_headers_clear {
            [[Clears all request headers.

              See @.request_header_add

              This should be called before dialing.
            ]]
        }

        request_headers_get {
            [[Returns an iterator to the key-value pairs for request headers]]
            return: iterator<ptr(Efl.Net.Http.Header)> @move @no_unused; [[Iterator to key value pairs]]
        }

        @property cookie_jar {
            [[This property sets the filename where to read and write cookies.

              By setting a file to load and store cookies, the
              internal cookie system will be activated, automatically
              handling HTTP headers such as 'Set-cookie:' and sending
              the appropriate cookies for a server.

              If a new, empty session is to be used, start with an
              empty or non-existent file such as one created with
              mkstemp() or tmpfile(). Alternatively use an
              empty string ("") to keep it in memory.

              If you want to start from a pre-existent cookie jar
              but do not want to modify it, first copy that file and
              then pass the new, temporary file.

              Likewise, if you want to store some cookies in the
              system, create a cookie jar and pass its path to this
              property.

              Note: that whenever this property is set, even if to the
              same value, it will flush all cookies to the previously
              set file, then erase all known cookies, then use the new
              file (if any).
            ]]
            get { }
            set { }
            values {
                path: string; [[Path to cookie jar]]
            }
        }
    }

    events {
        message,text: string; [[Received a text string message (opcode=0x1)]]
        message,binary: ptr(const(Eina.Slice)); [[Received a binary message (opcode=0x2)]]
        pong: string; [[Received a pong (opcode=0xA) with optional message/reason]]
        closed,reason: Efl.Net.Dialer_Websocket_Closed_Reason; [[Received a request to close the connection. It may be a reply/confirmation from a local request, see @.close_request, or some server-generated reason. After this point, no more messages are allowed to be sent and no more will be received. @Efl.Io.Closer.close will be called.]]
    }

    implements {
        Efl.Object.constructor;
        Efl.Object.invalidate;
        Efl.Object.destructor;
        Efl.Net.Dialer.dial;
        Efl.Net.Dialer.address_dial { get; set; }
        Efl.Net.Dialer.connected { get; set; }
        Efl.Net.Dialer.proxy { get; set; }
        Efl.Net.Dialer.timeout_dial { get; set; }
        Efl.Net.Socket.address_local { get; }
        Efl.Net.Socket.address_remote { get; set; }
        Efl.Io.Reader.read;
        Efl.Io.Reader.can_read { get; set; }
        Efl.Io.Reader.eos { get; set; }
        Efl.Io.Writer.write;
        Efl.Io.Writer.can_write { get; set; }
        Efl.Io.Closer.close;
        Efl.Io.Closer.closed { get; }
        Efl.Io.Closer.close_on_exec { get; set; }
        Efl.Io.Closer.close_on_invalidate { get; set; }
    }
}