ccan/io: initialize connection with an explicit I/O plan.
[ccan] / ccan / io / test / run-15-timeout.c
1 #include <ccan/io/io.h>
2 /* Include the C files directly. */
3 #include <ccan/io/poll.c>
4 #include <ccan/io/io.c>
5 #include <ccan/tap/tap.h>
6 #include <sys/wait.h>
7 #include <stdio.h>
8 #include <unistd.h>
9
10 struct data {
11         int state;
12         int timeout_usec;
13         bool timed_out;
14         char buf[4];
15 };
16
17
18 static struct io_plan no_timeout(struct io_conn *conn, struct data *d)
19 {
20         ok1(d->state == 1);
21         d->state++;
22         return io_close(conn, d);
23 }
24
25 static struct io_plan timeout(struct io_conn *conn, struct data *d)
26 {
27         ok1(d->state == 1);
28         d->state++;
29         d->timed_out = true;
30         return io_close(conn, d);
31 }
32
33 static void finish_ok(struct io_conn *conn, struct data *d)
34 {
35         ok1(d->state == 2);
36         d->state++;
37         io_break(d, io_idle());
38 }
39
40 static void init_conn(int fd, struct data *d)
41 {
42         struct io_conn *conn;
43
44         ok1(d->state == 0);
45         d->state++;
46
47         conn = io_new_conn(fd, io_read(d->buf, sizeof(d->buf), no_timeout, d),
48                            finish_ok, d);
49         io_timeout(conn, time_from_usec(d->timeout_usec), timeout, d);
50 }
51
52 static int make_listen_fd(const char *port, struct addrinfo **info)
53 {
54         int fd, on = 1;
55         struct addrinfo *addrinfo, hints;
56
57         memset(&hints, 0, sizeof(hints));
58         hints.ai_family = AF_UNSPEC;
59         hints.ai_socktype = SOCK_STREAM;
60         hints.ai_flags = AI_PASSIVE;
61         hints.ai_protocol = 0;
62
63         if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0)
64                 return -1;
65
66         fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
67                     addrinfo->ai_protocol);
68         if (fd < 0)
69                 return -1;
70
71         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
72         if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) {
73                 close(fd);
74                 return -1;
75         }
76         if (listen(fd, 1) != 0) {
77                 close(fd);
78                 return -1;
79         }
80         *info = addrinfo;
81         return fd;
82 }
83
84 int main(void)
85 {
86         struct data *d = malloc(sizeof(*d));
87         struct addrinfo *addrinfo;
88         struct io_listener *l;
89         int fd, status;
90
91         /* This is how many tests you plan to run */
92         plan_tests(20);
93         d->state = 0;
94         d->timed_out = false;
95         d->timeout_usec = 100000;
96         fd = make_listen_fd("65002", &addrinfo);
97         ok1(fd >= 0);
98         l = io_new_listener(fd, init_conn, d);
99         ok1(l);
100         fflush(stdout);
101
102         if (!fork()) {
103                 int i;
104
105                 io_close_listener(l);
106                 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
107                             addrinfo->ai_protocol);
108                 if (fd < 0)
109                         exit(1);
110                 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
111                         exit(2);
112                 signal(SIGPIPE, SIG_IGN);
113                 usleep(500000);
114                 for (i = 0; i < strlen("hellothere"); i++) {
115                         if (write(fd, "hellothere" + i, 1) != 1)
116                                 break;
117                 }
118                 close(fd);
119                 freeaddrinfo(addrinfo);
120                 free(d);
121                 exit(i);
122         }
123         ok1(io_loop() == d);
124         ok1(d->state == 3);
125         ok1(d->timed_out == true);
126         ok1(wait(&status));
127         ok1(WIFEXITED(status));
128         ok1(WEXITSTATUS(status) < sizeof(d->buf));
129
130         /* This one shouldn't time out. */
131         d->state = 0;
132         d->timed_out = false;
133         d->timeout_usec = 500000;
134         fflush(stdout);
135
136         if (!fork()) {
137                 int i;
138
139                 io_close_listener(l);
140                 fd = socket(addrinfo->ai_family, addrinfo->ai_socktype,
141                             addrinfo->ai_protocol);
142                 if (fd < 0)
143                         exit(1);
144                 if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0)
145                         exit(2);
146                 signal(SIGPIPE, SIG_IGN);
147                 usleep(100000);
148                 for (i = 0; i < strlen("hellothere"); i++) {
149                         if (write(fd, "hellothere" + i, 1) != 1)
150                                 break;
151                 }
152                 close(fd);
153                 freeaddrinfo(addrinfo);
154                 free(d);
155                 exit(i);
156         }
157         ok1(io_loop() == d);
158         ok1(d->state == 3);
159         ok1(d->timed_out == false);
160         ok1(wait(&status));
161         ok1(WIFEXITED(status));
162         ok1(WEXITSTATUS(status) >= sizeof(d->buf));
163
164         io_close_listener(l);
165         freeaddrinfo(addrinfo);
166         free(d);
167
168         /* This exits depending on whether all tests passed */
169         return exit_status();
170 }