Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/io/syncsocket.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
#ifndef _WIN32
4
    #include "unistd.h"
5
#endif
6
7
#if defined(_MSC_VER)
8
#define snprintf _snprintf
9
#endif
10
11
 /* Data that we keep for a socket-based handle. */
12
typedef struct {
13
    /* Start with same fields as a sync stream, since we will re-use most
14
     * of its logic. */
15
    MVMIOSyncStreamData ss;
16
17
    /* Status of the last connect attempt, if any. */
18
    int connect_status;
19
20
    /* Details of next connection to accept; NULL if none. */
21
    uv_stream_t *accept_server;
22
    int          accept_status;
23
} MVMIOSyncSocketData;
24
25
0
static void free_on_close_cb(uv_handle_t *handle) {
26
0
    MVM_free(handle);
27
0
}
28
0
static MVMint64 do_close(MVMThreadContext *tc, MVMIOSyncSocketData *data) {
29
0
    if (data->ss.handle) {
30
0
         uv_close((uv_handle_t *)data->ss.handle, free_on_close_cb);
31
0
         data->ss.handle = NULL;
32
0
    }
33
0
    if (data->ss.ds) {
34
0
        MVM_string_decodestream_destroy(tc, data->ss.ds);
35
0
        data->ss.ds = NULL;
36
0
    }
37
0
    return 0;
38
0
}
39
0
static MVMint64 close_socket(MVMThreadContext *tc, MVMOSHandle *h) {
40
0
    MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data;
41
0
    return do_close(tc, data);
42
0
}
43
44
0
static void gc_free(MVMThreadContext *tc, MVMObject *h, void *d) {
45
0
    MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)d;
46
0
    do_close(tc, data);
47
0
    MVM_string_decode_stream_sep_destroy(tc, &(data->ss.sep_spec));
48
0
    MVM_free(data);
49
0
}
50
51
/* Actually, it may return sockaddr_in6 as well; it's not a problem for us, because we just
52
 * pass is straight to uv, and the first thing it does is it looks at the address family,
53
 * but it's a thing to remember if someone feels like peeking inside the returned struct. */
54
0
struct sockaddr * MVM_io_resolve_host_name(MVMThreadContext *tc, MVMString *host, MVMint64 port) {
55
0
    char *host_cstr = MVM_string_utf8_encode_C_string(tc, host);
56
0
    struct sockaddr *dest;
57
0
    struct addrinfo *result;
58
0
    int error;
59
0
    char port_cstr[8];
60
0
    snprintf(port_cstr, 8, "%d", (int)port);
61
0
62
0
    error = getaddrinfo(host_cstr, port_cstr, NULL, &result);
63
0
    MVM_free(host_cstr);
64
0
    if (error == 0) {
65
0
        if (result->ai_addr->sa_family == AF_INET6) {
66
0
            dest = MVM_malloc(sizeof(struct sockaddr_in6));
67
0
            memcpy(dest, result->ai_addr, sizeof(struct sockaddr_in6));
68
0
        } else {
69
0
            dest = MVM_malloc(sizeof(struct sockaddr));
70
0
            memcpy(dest, result->ai_addr, sizeof(struct sockaddr));
71
0
        }
72
0
    }
73
0
    else {
74
0
        MVM_exception_throw_adhoc(tc, "Failed to resolve host name");
75
0
    }
76
0
    freeaddrinfo(result);
77
0
78
0
    return dest;
79
0
}
80
81
0
static void on_connect(uv_connect_t* req, int status) {
82
0
    ((MVMIOSyncSocketData *)req->data)->connect_status = status;
83
0
    uv_unref((uv_handle_t *)req->handle);
84
0
}
85
0
static void socket_connect(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port) {
86
0
    MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data;
87
0
    if (!data->ss.handle) {
88
0
        struct sockaddr *dest    = MVM_io_resolve_host_name(tc, host, port);
89
0
        uv_tcp_t        *socket  = MVM_malloc(sizeof(uv_tcp_t));
90
0
        uv_connect_t    *connect = MVM_malloc(sizeof(uv_connect_t));
91
0
        int status;
92
0
93
0
        data->ss.cur_tc = tc;
94
0
        connect->data   = data;
95
0
        if ((status = uv_tcp_init(tc->loop, socket)) == 0 &&
96
0
                (status = uv_tcp_connect(connect, socket, dest, on_connect)) == 0) {
97
0
            uv_ref((uv_handle_t *)socket);
98
0
            uv_run(tc->loop, UV_RUN_DEFAULT);
99
0
            status = data->connect_status;
100
0
        }
101
0
102
0
        MVM_free(connect);
103
0
        MVM_free(dest);
104
0
105
0
        data->ss.handle = (uv_stream_t *)socket; /* So can be cleaned up in close */
106
0
        if (status < 0)
107
0
            MVM_exception_throw_adhoc(tc, "Failed to connect: %s", uv_strerror(status));
108
0
    }
109
0
    else {
110
0
        MVM_exception_throw_adhoc(tc, "Socket is already bound or connected");
111
0
    }
112
0
}
113
114
0
static void on_connection(uv_stream_t *server, int status) {
115
0
    /* Stash data for a later accept call (safe as we specify a queue of just
116
0
     * one connection). Decrement reference count also. */
117
0
    MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)server->data;
118
0
    data->accept_server = server;
119
0
    data->accept_status = status;
120
0
    uv_unref((uv_handle_t *)server);
121
0
}
122
0
static void socket_bind(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port, MVMint32 backlog) {
123
0
    MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data;
124
0
    if (!data->ss.handle) {
125
0
        struct sockaddr *dest    = MVM_io_resolve_host_name(tc, host, port);
126
0
        uv_tcp_t        *socket  = MVM_malloc(sizeof(uv_tcp_t));
127
0
        int r;
128
0
129
0
        if ((r = uv_tcp_init(tc->loop, socket)) != 0 ||
130
0
                (r = uv_tcp_bind(socket, dest, 0)) != 0) {
131
0
            MVM_free(socket);
132
0
            MVM_free(dest);
133
0
            MVM_exception_throw_adhoc(tc, "Failed to bind: %s", uv_strerror(r));
134
0
        }
135
0
        MVM_free(dest);
136
0
137
0
        /* Start listening, but unref the socket so it won't get in the way of
138
0
         * other things we want to do on this event loop. */
139
0
        socket->data = data;
140
0
        if ((r = uv_listen((uv_stream_t *)socket, backlog, on_connection)) != 0) {
141
0
            MVM_free(socket);
142
0
            MVM_exception_throw_adhoc(tc, "Failed to listen: %s", uv_strerror(r));
143
0
        }
144
0
        uv_unref((uv_handle_t *)socket);
145
0
146
0
        data->ss.handle = (uv_stream_t *)socket;
147
0
    }
148
0
    else {
149
0
        MVM_exception_throw_adhoc(tc, "Socket is already bound or connected");
150
0
    }
151
0
}
152
153
static MVMObject * socket_accept(MVMThreadContext *tc, MVMOSHandle *h);
154
155
/* IO ops table, populated with functions. */
156
static const MVMIOClosable     closable      = { close_socket };
157
static const MVMIOEncodable    encodable     = { MVM_io_syncstream_set_encoding };
158
static const MVMIOSyncReadable sync_readable = { MVM_io_syncstream_set_separator,
159
                                                 MVM_io_syncstream_read_line,
160
                                                 MVM_io_syncstream_slurp,
161
                                                 MVM_io_syncstream_read_chars,
162
                                                 MVM_io_syncstream_read_bytes,
163
                                                 MVM_io_syncstream_eof };
164
static const MVMIOSyncWritable sync_writable = { MVM_io_syncstream_write_str,
165
                                                 MVM_io_syncstream_write_bytes,
166
                                                 MVM_io_syncstream_flush,
167
                                                 MVM_io_syncstream_truncate };
168
static const MVMIOSeekable          seekable = { MVM_io_syncstream_seek,
169
                                                 MVM_io_syncstream_tell };
170
static const MVMIOSockety            sockety = { socket_connect,
171
                                                 socket_bind,
172
                                                 socket_accept };
173
static const MVMIOOps op_table = {
174
    &closable,
175
    &encodable,
176
    &sync_readable,
177
    &sync_writable,
178
    NULL,
179
    NULL,
180
    NULL,
181
    &seekable,
182
    &sockety,
183
    NULL,
184
    NULL,
185
    NULL,
186
    NULL,
187
    gc_free
188
};
189
190
0
static MVMObject * socket_accept(MVMThreadContext *tc, MVMOSHandle *h) {
191
0
    MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data;
192
0
193
0
    while (!data->accept_server) {
194
0
        if (tc->loop != data->ss.handle->loop) {
195
0
            MVM_exception_throw_adhoc(tc, "Tried to accept() on a socket from outside its originating thread");
196
0
        }
197
0
        uv_ref((uv_handle_t *)data->ss.handle);
198
0
        MVM_gc_mark_thread_blocked(tc);
199
0
        uv_run(tc->loop, UV_RUN_DEFAULT);
200
0
        MVM_gc_mark_thread_unblocked(tc);
201
0
    }
202
0
203
0
    /* Check the accept worked out. */
204
0
    if (data->accept_status < 0) {
205
0
        MVM_exception_throw_adhoc(tc, "Failed to listen: unknown error");
206
0
    }
207
0
    else {
208
0
        uv_tcp_t *client    = MVM_malloc(sizeof(uv_tcp_t));
209
0
        uv_stream_t *server = data->accept_server;
210
0
        int r;
211
0
        uv_tcp_init(tc->loop, client);
212
0
        data->accept_server = NULL;
213
0
        if ((r = uv_accept(server, (uv_stream_t *)client)) == 0) {
214
0
            MVMOSHandle         * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO);
215
0
            MVMIOSyncSocketData * const data   = MVM_calloc(1, sizeof(MVMIOSyncSocketData));
216
0
            data->ss.handle   = (uv_stream_t *)client;
217
0
            data->ss.encoding = MVM_encoding_type_utf8;
218
0
            MVM_string_decode_stream_sep_default(tc, &(data->ss.sep_spec));
219
0
            result->body.ops  = &op_table;
220
0
            result->body.data = data;
221
0
            return (MVMObject *)result;
222
0
        }
223
0
        else {
224
0
            uv_close((uv_handle_t*)client, NULL);
225
0
            MVM_free(client);
226
0
            MVM_exception_throw_adhoc(tc, "Failed to accept: %s", uv_strerror(r));
227
0
        }
228
0
    }
229
0
}
230
231
0
MVMObject * MVM_io_socket_create(MVMThreadContext *tc, MVMint64 listen) {
232
0
    MVMOSHandle         * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO);
233
0
    MVMIOSyncSocketData * const data   = MVM_calloc(1, sizeof(MVMIOSyncSocketData));
234
0
    data->ss.handle   = NULL;
235
0
    data->ss.encoding = MVM_encoding_type_utf8;
236
0
    data->ss.translate_newlines = 0;
237
0
    MVM_string_decode_stream_sep_default(tc, &(data->ss.sep_spec));
238
0
    result->body.ops  = &op_table;
239
0
    result->body.data = data;
240
0
    return (MVMObject *)result;
241
0
}
242
243
0
MVMString * MVM_io_get_hostname(MVMThreadContext *tc) {
244
0
    char hostname[65];
245
0
    gethostname(hostname, 65);
246
0
    return MVM_string_ascii_decode_nt(tc, tc->instance->VMString, hostname);
247
0
}