summaryrefslogtreecommitdiff
path: root/src/lib/ecore_con/efl_net_socket.eo
blob: c1643cb46c853adfc938fb7ba39e380a25a69c24 (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
import efl_net_types;

// TODO: was using Eina.Binbuf, but asked to change to simpler types
struct Efl.Net.Socket.Receive_Event {
    data: const(uint8) *;
    size: size;
}

struct Efl.Net.Socket.Sent_Event {
    //blob: Eina.Blob; [[blob with data given to @.send]]
    size: size; [[size that was sent, if no error, matches blob's size, otherwise states partial operation.]]
    error: Eina.Error; [[if operation failed, notifies the error that happened]]
}

class Efl.Net.Socket (Efl.Loop_User) {
    [[Abstract class representing a network connection socket.

      A base socket is an already established connection and can send
      data (see @.send method), as well as notify about incoming data
      (see \@ref EFL_NET_SOCKET_EVENT_RECEIVED event).

      When the connection is closed, the socket will be automatically
      deleted and the Eo event "del" is to be used.

      @since 1.19
    ]]

    events {
        //received @hot: Eina.Binbuf; [[Data is available to read,
        received @hot: Efl.Net.Socket.Receive_Event; [[

                                      TODO: Only when using Eina.Binbuf:

                                      Data is available to read,
                                      if consumed use eina_binbuf_remove().

                                      To implement input flow control
                                      used a fixed size receive buffer
                                      (@.receive_buffer_size). When
                                      the buffer size is fully filled,
                                      the socket will stop reading
                                      more data.
                                    ]]
        sent: Efl.Net.Socket.Sent_Event; [[Data specified by blob was sent.]]

        drained; [[All pending data was sent.

                 This event is to implement flow control. To
                 avoid saturating the socket, listen this event
                 and only send more data once it's emitted.

                 Alternatively one can specify
                 @.send_buffer_size and check for @.send return
                 code, if it's "no space left", queue locally
                 the data, stop producing more and wait for
                 \@ref EFL_NET_SOCKET_EVENT_SENT
                 event in order to resume it.
               ]]

        closed; [[Socket was closed]]
        timedout; [[Socket was inactive and timed out]] /* maybe just use error event for that? */

        error: Eina.Error; [[An error occurred and the socket will be closed.

                             The error may be in either read, send or
                             socket becoming invalid (remote peer
                             closed the connection). If it's on send,
                             then it will be informed also on
                             \@ref EFL_NET_SOCKET_EVENT_SENT
                             event.

                             TODO: auto del the object?
                           ]]
    }

    methods {
        adopt {
            [[Constructor-only method that is used to initialize the new object owning a pre-existent file descriptor.

              Usually sockets are created from a specific dialer, such
              as @Efl.Net.Dialer.TCP, @Efl.Net.Dialer.UDP or
              @Efl.Net.Dialer.Unix, or internally from a server such
              as @Efl.Net.Server.TCP, @Efl.Net.Server.UDP or
              @Efl.Net.Server.Unix.

              This method is useful if you receive the filedescriptor
              from elsewhere.
              ]]
            params {
                @in fd: int; [[The Filedescriptor to adopt]]
            }
        }

        send {
            [[Queue data to be sent to remote.

              The API is asynchronous, thus data won't be immediately
              sent to the kernel, instead it will be queued locally
              and when the kernel can do a non-blocking operation,
              then it will be dispatched.

              The maximum amount of bytes to be sent is defined with
              @.send_buffer_size, with current usage reported as
              @.send_buffer_usage.

              If @.send_buffer_usage and the new blob size exceeds
              @.send_buffer_size, then an error (ENOSPC XXX TODO EINA_ERROR...)
              is returned immediately, no references are taken to the
              blob and no \@ref EFL_NET_SOCKET_EVENT_SENT
              event will be dispatched for this blob.

              If no error is returned, then a reference is kept to the
              blob and once it's fully sent, the
              \@ref EFL_NET_SOCKET_EVENT_SENT
              event will be dispatched with size parameter matching
              the blob's size and error will be zero. If some error
              occurs during the send syscall operation, the
              \@ref EFL_NET_SOCKET_EVENT_SENT
              event will be dispatched with size parameter less then
              blob's size and error will be non-zero. In all cases,
              after the \@ref EFL_NET_SOCKET_EVENT_SENT event is
              dispatched, the reference to the blob will be released.

              If the object is deleted with pending blobs, the
              \@ref EFL_NET_SOCKET_EVENT_SENT
              event will be dispatched with an error notifying
              cancellation (ECANCELED XXX TODO EINA_ERROR...)

              When all pending blobs were fully sent, then
              \@ref EFL_NET_SOCKET_EVENT_DRAINED
              event is dispatched.
            ]]
            params {
                //@in data: Eina.Blob; [[data to queue for sending]]
                @in data: const(uint8) * @nonull;
                @in size: size;
            }

            return: Eina.Error (0); [[0 on success,
                                      ENOSPC XXX TODO EINA_ERROR if no space left]]
        }

        flush {
            [[Try to send as much as data without blocking.

              If all data was sent, then
              \@ref EFL_NET_SOCKET_EVENT_DRAINED
              event will be dispatched and true is returned.

              If the kernel can't sent more data and the operation
              would block waiting, then false is returned.

              If really all data must be sent, the user should busy
              wait based on @.send_buffer_usage and call @.flush in a
              loop.
            ]]
            return: bool (false); [[true if all data was sent]]
        }

        close {
            [[Closes the socket, discarding all read data and pending data to be sent.

              If pending data was discarded, then
              \@ref EFL_NET_SOCKET_EVENT_SENT
              will be called with \@ref EFL_NET_ERROR_CANCELED.

              If all data must be delivered prior to close, do it from
              \@ref EFL_NET_SOCKET_EVENT_DRAINED
              event callback.
            ]]
        }

        steal_fd {
            [[Steal the file descriptor and make this connection shallow.

              A shallow socket can't do any real operation and must be
              deleted.

              XXX TODO: auto delete?
            ]]
            return: int (-1); [[the internal file descriptor]]
        }

        @property address_local {
            [[The local IP or unix-local address.

              This is analogous to getsockname(), it must return the
              local address and port for IP connetions, the path for
              unix socket.
            ]]
            get {
            }
            values {
                address: string;
            }
        }

        @property address_remote {
            [[The remote IP or unix-local address.

              This is Analogous to getpeername(), it must return the
              remote (peer) address and port for IP connections, the
              path for unix socket.

              For IP addresses, the returned value is the final IP
              address, so it's already resolved.
            ]]
            get {
            }
            values {
                address: string;
            }
        }

        @property flags {
            [[Bitwise OR of flags to used on this socket.]]
            values {
                flags: Efl.Net.Socket_Flags;
            }
        }

        @property connected {
            [[If the socket is still connected.]]
            get {
            }
            values {
                connected: bool;
            }
        }

        @property send_buffer_usage {
            [[How many bytes are queued to be sent.

              If @.send_buffer_size >= 0, then this must be <=
              @.send_buffer_size.
            ]]
            get {
            }
            values {
                bytes: size;
            }
        }

        @property send_buffer_size {
            [[Amount of bytes to use when sending data.
              0 is unlimited,
              > 0 is an upper limit of queued bytes before
              @.send returns ENOSPC]]
            values {
                bytes: size;
            }
        }

        @property receive_buffer_size {
            [[Amount of bytes to use when receiving data.
              0 is unlimited, > 0 is fixed size.]]
            values {
                bytes: size;
            }
        }

        @property timeout {
            [[Timeout (in seconds) to close the connection.

            If nothing is sent or received until this amount of
            seconds is elapsed, then the connection will be
            automatically closed and the socket object will be
            deleted.
            ]]
            values {
                timeout: double;
            }
        }
    }

    implements {
        Eo.Base.constructor;
        Eo.Base.destructor;
        Eo.Base.finalize;
        // TODO: needed for Loop_User? Eo.Base.parent.set;
    }

    constructors {
        .adopt;
    }
}