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
|
#ifndef OSTREAM_WRAPPER_H
#define OSTREAM_WRAPPER_H
#include "ostream-private.h"
/* The wrapper output stream allows turning any form* of activity involving data
output into a standard Dovecot output stream. The wrapper output stream can
operate both in blocking and non-blocking mode. When the wrapped activity is
non-blocking, a blocking wrapper output stream will implicitly run its own
ioloop.
It is possible to have the wrapper output stream object available even before
the data can be written anywhere, even before any form of output object (a
connection) exists. In that case, any data written to the wrapper stream is
buffered until the buffer is full. Once that happens, the stream will block
or refuse writes until the underlying output becomes available.
The wrapper output stream is not meant to be used directly. Instead, it is
to be used as part of the implementation of an application-specific output
stream. The wrapper output stream serves as the means to prevent code
duplication between similar output stream implementations. It defines several
methods that need to be implemented by the application-specific output
stream.
* Currently, the wrapper stream still expects an output stream object when
data is to be written somewhere, but that should be easily circumvented
once such behavior is needed (FIXME).
*/
struct wrapper_ostream {
struct ostream_private ostream;
struct event *event;
/* Called when the implementation should start making the parent output
stream available, e.g. connect to the server. This happens when data
was written to the wrapper ostream (when it is corked this only
happens when the wrapper ostream buffer is full or the wrapper
ostream is finished). */
void (*output_start)(struct wrapper_ostream *wostream);
/* Returns TRUE when the output is ready for data. */
bool (*output_ready)(struct wrapper_ostream *wostream);
/* Called when an error occurred while writing to the output stream. */
void (*output_error)(struct wrapper_ostream *wostream);
/* Called when the wrapper ostream was finished using o_stream_finish()
and the wrapper ostream buffer is empty. Also, the parent output
was flushed successfully. */
int (*output_finish)(struct wrapper_ostream *wostream);
/* Called when the wrapper ostream does not need write to parent output
stream. This is will e.g. drop the parent output's flush callback or
equivalent notification mechanism. */
void (*output_halt)(struct wrapper_ostream *wostream);
/* Called when the wrapper ostream has data available for the parent
output and wants wrapper_ostream_continue() to be called when the
parent stream is writeable. */
void (*output_resume)(struct wrapper_ostream *wostream);
/* Update the timeouts. The sender_blocking parameter indicates which
side of the data transfer is blocking, so whether a timeout needs to
be set for limiting the time other side is not doing anything. */
void (*output_update_timeouts)(struct wrapper_ostream *wostream,
bool sender_blocking);
/* Called before and after running ioloop for performing blocking I/O
wait. Use these vfuncs to switch to and from the temporary ioloop. */
struct ioloop *(*wait_begin)(struct wrapper_ostream *wostream,
struct ioloop *ioloop);
void (*wait_end)(struct wrapper_ostream *wostream,
struct ioloop *prev_ioloop);
/* Called before and after running the flush callback for the ostream.
*/
void (*callback_pre)(struct wrapper_ostream *wostream);
void (*callback_post)(struct wrapper_ostream *wostream);
/* Called when the ostream is switched to a different ioloop. */
void (*switch_ioloop_to)(struct wrapper_ostream *wostream,
struct ioloop *ioloop);
/* Called when the wrapper ostream is forcibly closed using
o_stream_close() (or indirectly through e.g. o_stream_destroy()). */
void (*close)(struct wrapper_ostream *wostream);
/* Called when the ostream is destroyed. */
void (*destroy)(struct wrapper_ostream *wostream);
buffer_t *buffer; // FIXME: use a ringbuffer instead (file_ostream)
/* The (parent) output stream. */
struct ostream *output;
/* The ioloop used while flushing/sending output for when the wrapper
ostream is blocking. */
struct ioloop *flush_ioloop;
/* Error set using wrapper_ostream_return_error(). This is returned to
the application once it continues using the wrapper ostream. */
char *pending_error;
int pending_errno;
/* Timeout for delayed execution of wrapper_ostream_continue(). */
struct timeout *to_event;
/* Output was started (output_start() vfunc was called). */
bool output_started:1;
/* Output was finished (output_finish() vfunc was called). */
bool output_finished:1;
/* Output was was closed somehow. This means that the output is no
longer available. This is not the same as the ostream close flag. */
bool output_closed:1;
/* Output was closed directly or indirectly by the application action.
*/
bool output_closed_api:1;
bool flush_pending:1;
bool flush_waiting:1;
bool flushing:1;
bool continuing:1;
bool returned_error:1;
};
/* Create the wrapper output stream. This function calls o_stream_create()
internally. The initial maximum buffer size is set to max_buffer_size. When
blocking is TRUE, a blocking output stream will be created. The provided
event is used internally for debug logging. */
struct ostream *
wrapper_ostream_create(struct wrapper_ostream *wostream,
size_t max_buffer_size, bool blocking,
struct event *event) ATTR_NULL(4);
/* Continue sending output. Returns 1 if all buffered data is sent so far,
0 if not, and -1 if an error occurred. */
int wrapper_ostream_continue(struct wrapper_ostream *wostream);
/* Trigger an (asynchronous) flush on the output stream. */
void wrapper_ostream_trigger_flush(struct wrapper_ostream *wostream);
/* This function returns the size of the data buffered in the wrapper stream,
but only when the output stream is finished using o_stream_finish(). When the
output stream is finished, the data is complete and this function returns
TRUE and size_r is set to the size. If it is not complete, this function
returns FALSE and size_r is not assigned. This function is meant to be called
just before sending the first block of data internally for deciding between
sending the data using a chunked transfer encoding or, when it is already
complete, as a single blob with known size. E.g., for HTTP this is the choice
between sending the message using the Transfer-Encoding: chunked header or
the Content-Length header. */
bool wrapper_ostream_get_buffered_size(struct wrapper_ostream *wostream,
uoff_t *size_r);
/* Call this when the underlying output stream first becomes available. */
void wrapper_ostream_output_available(struct wrapper_ostream *wostream,
struct ostream *output);
/* Call this to notify the wrapper that the underlying output is destroyed and
no more data can be written ever. */
void wrapper_ostream_output_destroyed(struct wrapper_ostream *wostream);
/* Call this to notify the wrapper that an error has occurred. It will be
returned as such for the next stream write/flush and subsequent
o_stream_get_error(). */
void wrapper_ostream_set_error(struct wrapper_ostream *wostream,
int stream_errno, const char *stream_error);
/* Notify the application immediately about any error condition set earlier
using wrapper_ostream_set_error() by calling the ostream flush callback
right now.
*/
void wrapper_ostream_notify_error(struct wrapper_ostream *wostream);
#endif
|