summaryrefslogtreecommitdiff
path: root/reference/c/net/src/net_io_buffered.c
blob: a3aa2f895fbc6678cb3f149431176867331843d6 (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
#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1

#include <stdio.h>

#include <Eina.h>
#include <Efl_Net.h>

/*
 * Efl.Net buffered input/output examples.
 *
 * This example builds on the net_io example by using a buffered_stream to
 * simplify the logic. This helpfully provides the input and output queues
 * and a copier internally. They can be accessed from the buffered stream
 * if required but as demonstrated here that is likely not necessary.
 */

static Eina_List *_commands = NULL;
static Eina_Slice _delimiter;
static Efl_Net_Dialer *_dialer = NULL;
static Efl_Io_Buffered_Stream *_stream = NULL;

static void
_quit(int retval)
{
   if (_stream)
     {
        efl_io_closer_close(_stream);
        efl_del(_stream);
     }

   if (_dialer)
     efl_del(_dialer);

   efl_exit(retval);
}

static void
_command_next(void)
{
   Eina_Slice slice;
   char *cmd;

   if (!_commands)
     {
        efl_io_buffered_stream_eos_mark(_stream);
        return;
     }

   cmd = _commands->data;
   _commands = eina_list_remove_list(_commands, _commands);

   slice = (Eina_Slice)EINA_SLICE_STR(cmd);
   efl_io_writer_write(_stream, &slice, NULL);
   fprintf(stderr, "INFO: sent '" EINA_SLICE_STR_FMT "'\n",
           EINA_SLICE_STR_PRINT(slice));

   /* don't use _delimiter directly, 'len' may be changed! */
   slice = _delimiter;
   efl_io_writer_write(_stream, &slice, NULL);
}

static void
_stream_line(void *data EINA_UNUSED, const Efl_Event *event)
{
   Eina_Slice slice = efl_io_buffered_stream_slice_get(event->object);

   // Can be caused when we issue efl_io_buffered_stream_clear()
   if (slice.len == 0) return;

   /*
    * If the server didn't send us the line terminator and closed the
    * connection (ie: efl_io_reader_eos_get() == true) or if the buffer
    * limit was reached then we may have a line without a trailing delimiter.
    */
   if (eina_slice_endswith(slice, _delimiter))
     slice.len -= _delimiter.len;

   fprintf(stderr, "INFO: received '" EINA_SLICE_STR_FMT "'\n",
           EINA_SLICE_STR_PRINT(slice));

   efl_io_buffered_stream_clear(event->object);
   _command_next();
}

static void
_dialer_connected(void *data EINA_UNUSED, const Efl_Event *event)
{
   fprintf(stderr, "INFO: connected to %s (%s)\n",
           efl_net_dialer_address_dial_get(event->object),
           efl_net_socket_address_remote_get(event->object));

   _command_next();
}

static void
_stream_done(void *data EINA_UNUSED, const Efl_Event *event)
{
   fprintf(stderr, "INFO: %s done\n", efl_name_get(event->object));

   _quit(EXIT_SUCCESS);
}

static void
_stream_error(void *data EINA_UNUSED, const Efl_Event *event)
{
   const Eina_Error *perr = event->info;

   fprintf(stderr, "INFO: %s error: #%d '%s'\n",
           efl_name_get(event->object), *perr, eina_error_msg_get(*perr));

   _quit(EXIT_FAILURE);
}

EFL_CALLBACKS_ARRAY_DEFINE(stream_cbs,
                           { EFL_IO_BUFFERED_STREAM_EVENT_LINE, _stream_line },
                           { EFL_IO_READER_EVENT_EOS, _stream_done },
                           { EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _stream_error });

EAPI_MAIN void
efl_main(void *data EINA_UNUSED, const Efl_Event *ev)
{
   char *address = "example.com:80";
   unsigned long buffer_limit = 128;
   Eina_Error err;
   Efl_Loop *loop;

   _commands = eina_list_append(_commands, "HEAD / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
   _delimiter = (Eina_Slice)EINA_SLICE_STR("\r\n");

   /*
    * some objects such as the Efl.Io.Copier and Efl.Net.Dialer.Tcp
    * depend on main loop, thus their parent must be a loop
    * provider. We use the loop passed to our main method.
    */
   loop = ev->object;

   /* The TCP client to use to send/receive network data */
   _dialer = efl_add(EFL_NET_DIALER_TCP_CLASS, loop,
                     efl_name_set(efl_added, "dialer"),
                     efl_event_callback_add(efl_added, EFL_NET_DIALER_EVENT_CONNECTED, _dialer_connected, NULL));
   if (!_dialer)
     {
        fprintf(stderr, "ERROR: could not create Efl_Net_Dialer_Tcp\n");
        _quit(EXIT_FAILURE);
     }

   /*
    * Without the buffered stream we'd have to create two Efl.Io.Queue
    * ourselves, as well as two Efl.Io.Copier to link them with the
    * dialer.
    *
    * Our example's usage is to write each command at once followed by
    * the line_delimiter, then wait for a reply from the server, then
    * write another.
    *
    * On incoming data we peek at it with slice_get() and then clear().
    */
   _stream = efl_add(EFL_IO_BUFFERED_STREAM_CLASS, loop,
                     efl_name_set(efl_added, "stream"),
                     efl_io_buffered_stream_inner_io_set(efl_added, _dialer), /* mandatory! */
                     efl_io_buffered_stream_line_delimiter_set(efl_added, _delimiter),
                     efl_io_buffered_stream_max_queue_size_input_set(efl_added, buffer_limit),
                     efl_io_buffered_stream_max_queue_size_output_set(efl_added, buffer_limit),
                     efl_event_callback_array_add(efl_added, stream_cbs(), NULL));

   err = efl_net_dialer_dial(_dialer, address);
   if (err)
     {
        fprintf(stderr, "ERROR: could not dial %s: %s\n",
                address, eina_error_msg_get(err));
        _quit(EXIT_FAILURE);
     }
}
EFL_MAIN()