summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/uvloop/handles/tcp.pyx
blob: d5fe827a07547be356976983dd93162620594f17 (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
cdef __tcp_init_uv_handle(UVStream handle, Loop loop, unsigned int flags):
    cdef int err

    handle._handle = <uv.uv_handle_t*>PyMem_RawMalloc(sizeof(uv.uv_tcp_t))
    if handle._handle is NULL:
        handle._abort_init()
        raise MemoryError()

    err = uv.uv_tcp_init_ex(handle._loop.uvloop,
                            <uv.uv_tcp_t*>handle._handle,
                            flags)
    if err < 0:
        handle._abort_init()
        raise convert_error(err)

    handle._finish_init()


cdef __tcp_bind(UVStream handle, system.sockaddr* addr, unsigned int flags):
    cdef int err
    err = uv.uv_tcp_bind(<uv.uv_tcp_t *>handle._handle,
                         addr, flags)
    if err < 0:
        exc = convert_error(err)
        raise exc


cdef __tcp_open(UVStream handle, int sockfd):
    cdef int err
    err = uv.uv_tcp_open(<uv.uv_tcp_t *>handle._handle,
                         <uv.uv_os_sock_t>sockfd)
    if err < 0:
        exc = convert_error(err)
        raise exc


cdef __tcp_get_socket(UVSocketHandle handle):
    cdef:
        int buf_len = sizeof(system.sockaddr_storage)
        int fileno
        int err
        system.sockaddr_storage buf

    fileno = handle._fileno()

    err = uv.uv_tcp_getsockname(<uv.uv_tcp_t*>handle._handle,
                                <system.sockaddr*>&buf,
                                &buf_len)
    if err < 0:
        raise convert_error(err)

    return PseudoSocket(buf.ss_family, uv.SOCK_STREAM, 0, fileno)


@cython.no_gc_clear
cdef class TCPServer(UVStreamServer):

    @staticmethod
    cdef TCPServer new(Loop loop, object protocol_factory, Server server,
                       unsigned int flags,
                       object backlog,
                       object ssl,
                       object ssl_handshake_timeout,
                       object ssl_shutdown_timeout):

        cdef TCPServer handle
        handle = TCPServer.__new__(TCPServer)
        handle._init(loop, protocol_factory, server, backlog,
                     ssl, ssl_handshake_timeout, ssl_shutdown_timeout)
        __tcp_init_uv_handle(<UVStream>handle, loop, flags)
        return handle

    cdef _new_socket(self):
        return __tcp_get_socket(<UVSocketHandle>self)

    cdef _open(self, int sockfd):
        self._ensure_alive()
        try:
            __tcp_open(<UVStream>self, sockfd)
        except Exception as exc:
            self._fatal_error(exc, True)
        else:
            self._mark_as_open()

    cdef bind(self, system.sockaddr* addr, unsigned int flags=0):
        self._ensure_alive()
        try:
            __tcp_bind(<UVStream>self, addr, flags)
        except Exception as exc:
            self._fatal_error(exc, True)
        else:
            self._mark_as_open()

    cdef UVStream _make_new_transport(self, object protocol, object waiter,
                                      object context):
        cdef TCPTransport tr
        tr = TCPTransport.new(self._loop, protocol, self._server, waiter,
                              context)
        return <UVStream>tr


@cython.no_gc_clear
cdef class TCPTransport(UVStream):

    @staticmethod
    cdef TCPTransport new(Loop loop, object protocol, Server server,
                          object waiter, object context):

        cdef TCPTransport handle
        handle = TCPTransport.__new__(TCPTransport)
        handle._init(loop, protocol, server, waiter, context)
        __tcp_init_uv_handle(<UVStream>handle, loop, uv.AF_UNSPEC)
        handle.__peername_set = 0
        handle.__sockname_set = 0
        handle._set_nodelay()
        return handle

    cdef _set_nodelay(self):
        cdef int err
        self._ensure_alive()
        err = uv.uv_tcp_nodelay(<uv.uv_tcp_t*>self._handle, 1)
        if err < 0:
            raise convert_error(err)

    cdef _call_connection_made(self):
        # asyncio saves peername & sockname when transports are instantiated,
        # so that they're accessible even after the transport is closed.
        # We are doing the same thing here, except that we create Python
        # objects lazily, on request in get_extra_info()

        cdef:
            int err
            int buf_len

        buf_len = sizeof(system.sockaddr_storage)
        err = uv.uv_tcp_getsockname(<uv.uv_tcp_t*>self._handle,
                                    <system.sockaddr*>&self.__sockname,
                                    &buf_len)
        if err >= 0:
            # Ignore errors, this is an optional thing.
            # If something serious is going on, the transport
            # will crash later (in roughly the same way how
            # an asyncio transport would.)
            self.__sockname_set = 1

        buf_len = sizeof(system.sockaddr_storage)
        err = uv.uv_tcp_getpeername(<uv.uv_tcp_t*>self._handle,
                                    <system.sockaddr*>&self.__peername,
                                    &buf_len)
        if err >= 0:
            # Same as few lines above -- we don't really care
            # about error case here.
            self.__peername_set = 1

        UVBaseTransport._call_connection_made(self)

    def get_extra_info(self, name, default=None):
        if name == 'sockname':
            if self.__sockname_set:
                return __convert_sockaddr_to_pyaddr(
                    <system.sockaddr*>&self.__sockname)
        elif name == 'peername':
            if self.__peername_set:
                return __convert_sockaddr_to_pyaddr(
                    <system.sockaddr*>&self.__peername)
        return super().get_extra_info(name, default)

    cdef _new_socket(self):
        return __tcp_get_socket(<UVSocketHandle>self)

    cdef bind(self, system.sockaddr* addr, unsigned int flags=0):
        self._ensure_alive()
        __tcp_bind(<UVStream>self, addr, flags)

    cdef _open(self, int sockfd):
        self._ensure_alive()
        __tcp_open(<UVStream>self, sockfd)

    cdef connect(self, system.sockaddr* addr):
        cdef _TCPConnectRequest req
        req = _TCPConnectRequest(self._loop, self)
        req.connect(addr)


cdef class _TCPConnectRequest(UVRequest):
    cdef:
        TCPTransport transport
        uv.uv_connect_t _req_data

    def __cinit__(self, loop, transport):
        self.request = <uv.uv_req_t*>&self._req_data
        self.request.data = <void*>self
        self.transport = transport

    cdef connect(self, system.sockaddr* addr):
        cdef int err
        err = uv.uv_tcp_connect(<uv.uv_connect_t*>self.request,
                                <uv.uv_tcp_t*>self.transport._handle,
                                addr,
                                __tcp_connect_callback)
        if err < 0:
            exc = convert_error(err)
            self.on_done()
            raise exc


cdef void __tcp_connect_callback(
    uv.uv_connect_t* req,
    int status,
) noexcept with gil:
    cdef:
        _TCPConnectRequest wrapper
        TCPTransport transport

    wrapper = <_TCPConnectRequest> req.data
    transport = wrapper.transport

    if status < 0:
        exc = convert_error(status)
    else:
        exc = None

    try:
        transport._on_connect(exc)
    except BaseException as ex:
        wrapper.transport._fatal_error(ex, False)
    finally:
        wrapper.on_done()