mmlib/mmat: replace some variables by literal constants
[mmondor.git] / tests / bktr_httpd / sendq.c
1 /* $Id: sendq.c,v 1.9 2012/02/17 17:15:42 mmondor Exp $ */
2
3 /*
4 * Copyright (c) 2008, Matthew Mondor
5 * ALL RIGHTS RESERVED.
6 */
7
8
9 #include <errno.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <syslog.h>
15 #include <unistd.h>
16
17 #include "types.h"
18 #include "sendq.h"
19 #include "config.h"
20
21
22 static bool qitem_ctor(pnode_t *);
23 static void qitem_dtor(pnode_t *);
24
25
26 static pool_t qitem_pool, qnode_pool;
27
28
29 bool
30 sendq_init(void)
31 {
32
33 if (!pool_init(&qitem_pool, "qitem_pool", malloc, free,
34 qitem_ctor, qitem_dtor, sizeof(qitem_t), 4096 / sizeof(qitem_t),
35 1, 0))
36 return false;
37 if (!pool_init(&qnode_pool, "qnode_pool", malloc, free,
38 NULL, NULL, sizeof(qnode_t), 65536 / sizeof(qnode_t), 1, 0))
39 return false;
40
41 return true;
42 }
43
44 qitem_t *
45 qitem_new(void)
46 {
47 qitem_t *qi;
48
49 if ((qi = (qitem_t *)pool_alloc(&qitem_pool, false)) != NULL)
50 qi->refcnt = 0;
51
52 return qi;
53 }
54
55 /*
56 * Initializes the qitem headers. <size> should be the size of the image in
57 * bytes, while <type> should be a mime type, such as "image/jpeg".
58 * The image data is expected to be placed at qi->image.
59 */
60 void
61 qitem_frame_init(qitem_t *qi, unsigned int size, const char *type)
62 {
63 static const char header1[] =
64 "HTTP/1.1 200 OK\r\n"
65 "Server: " SERVER_VERSION "\r\n"
66 "Connection: close\r\n"
67 "Cache-Control: no-cache\r\n"
68 "Content-Type: multipart/x-mixed-replace; boundary=--frameboundary\r\n";
69 char buf[QITEM_HSIZE];
70 int hlen;
71
72 hlen = snprintf(buf, QITEM_HSIZE - 1,
73 "%s"
74 "\r\n"
75 "--frameboundary\r\n"
76 "Content-Length: %u\r\n"
77 "Content-Type: %s\r\n"
78 "\r\n",
79 header1, size, type);
80
81 qi->header1 = &qi->image[-hlen];
82 qi->header2 = &qi->header1[sizeof(header1)];
83 (void) memcpy(qi->header1, buf, hlen);
84 qi->end = &qi->image[size];
85 }
86
87 void
88 qitem_destroy(qitem_t *qi)
89 {
90
91 (void) pool_free((pnode_t *)qi);
92 }
93
94 static bool
95 qitem_ctor(pnode_t *pn)
96 {
97 qitem_t *qi = (qitem_t *)pn;
98
99 if ((qi->data = malloc(QITEM_SIZE)) == NULL)
100 return false;
101 qi->image = &qi->data[QITEM_HSIZE];
102
103 return true;
104 }
105
106 static void
107 qitem_dtor(pnode_t *pn)
108 {
109 qitem_t *qi = (qitem_t *)pn;
110
111 free(qi->data);
112 }
113
114 /*
115 * Creates a new sendq node from the shared qitem, and sets it up to begin at
116 * either qi->header1 or qi->header2, depending on if it's the first node
117 * queued for it or if it's a subsequent one.
118 * If <first> is NULL, qn->ptr is set at qi->data, which assumes that the
119 * block consists of a general data buffer rather than an image frame.
120 */
121 qnode_t *
122 qnode_new(qitem_t *qi, bool *first)
123 {
124 qnode_t *qn;
125
126 if ((qn = (qnode_t *)pool_alloc(&qnode_pool, false)) != NULL) {
127 qn->qitem = qi;
128 qi->refcnt++;
129 if (first != NULL) {
130 if (*first) {
131 qn->ptr = qi->header1;
132 *first = false;
133 } else
134 qn->ptr = qi->header2;
135 qn->close = false;
136 } else {
137 qn->ptr = qi->data;
138 qn->close = true;
139 }
140 }
141
142 return qn;
143 }
144
145 /*
146 * Destroys this sendq node, and automatically destroy the wrapped shared
147 * qitem if no more qnodes reference to it.
148 */
149 void
150 qnode_destroy(qnode_t *qn)
151 {
152
153 if (--qn->qitem->refcnt == 0)
154 qitem_destroy(qn->qitem);
155
156 (void) pool_free((pnode_t *)qn);
157 }
158
159 /*
160 * Attempts to flush the queue. Returns 0 if the queue could be completely
161 * flushed but client should remain connected, EAGAIN if it only could
162 * partially be sent, or EPIPE on error. EAGAIN means that the client cannot
163 * read fast enough. Returns ECONNRESET if all data was successfully flushed
164 * but that connection with the client should be closed.
165 * Frees any flushed data, but keeps unflushed and incompletely flushed
166 * entries.
167 */
168 int
169 sendq_flush(int fd, list_t *q)
170 {
171 qnode_t *qn, *next;
172 size_t len, wlen;
173 bool cl = false;
174
175 for (qn = (qnode_t *)DLIST_TOP(q); qn != NULL && !cl; qn = next) {
176 next = (qnode_t *)DLIST_NEXT((node_t *)qn);
177
178 len = qn->qitem->end - qn->ptr;
179 wlen = write(fd, qn->ptr, qn->qitem->end - qn->ptr);
180 if (wlen == -1) {
181 if (errno == EAGAIN)
182 return EAGAIN;
183 return EPIPE;
184 }
185 if (wlen < len) {
186 qn->ptr += wlen;
187 return EAGAIN;
188 }
189
190 if (qn->close)
191 cl = true;
192 DLIST_UNLINK(q, (node_t *)qn);
193 qnode_destroy(qn);
194 }
195
196 return (cl ? ECONNRESET : 0);
197 }
198
199 /*
200 * Purges the sendq buffers. If <all> is true, every entry is destroyed.
201 * Otherwise, the first entry is preserved (assumed to be incompletely sent)
202 * and all other entries discarded. Useful to call before closing connection
203 * with a client or when sendq_flush() returns EAGAIN, in which case the
204 * client is not able to read() fast enough.
205 */
206 void
207 sendq_purge(list_t *q, bool all)
208 {
209 qnode_t *qn, *next;
210
211 if (!all) {
212 /* Skip first entry if any */
213 if ((qn = (qnode_t *)DLIST_TOP(q)) != NULL)
214 qn = (qnode_t *)DLIST_NEXT((node_t *)qn);
215 } else
216 qn = (qnode_t *)DLIST_TOP(q);
217
218 /* Purge any remaining entries */
219 for ( ; qn != NULL; qn = next) {
220 next = (qnode_t *)DLIST_NEXT((node_t *)qn);
221
222 DLIST_UNLINK(q, (node_t *)qn);
223 qnode_destroy(qn);
224 }
225 }
226
227 /*
228 * Utility function to queue custom data to a sendq.
229 * At most QITEM_SIZE bytes may be queued.
230 */
231 bool
232 sendq_page(list_t *q, const char *fmt, ...)
233 {
234 qitem_t *qi;
235 qnode_t *qn;
236 va_list ap;
237 int len;
238
239 if ((qi = qitem_new()) != NULL) {
240 va_start(ap, fmt);
241 len = vsnprintf((char *)qi->data, QITEM_SIZE - 1, fmt, ap);
242 va_end(ap);
243 qi->end = &qi->data[len];
244
245 if ((qn = qnode_new(qi, NULL)) != NULL) {
246 DLIST_APPEND(q, (node_t *)qn);
247 return true;
248 }
249 qitem_destroy(qi);
250 }
251
252 return false;
253 }