mmlib/mmat: replace some variables by literal constants
[mmondor.git] / tests / bktr_httpd / sendq.c
CommitLineData
99a016b2 1/* $Id: sendq.c,v 1.9 2012/02/17 17:15:42 mmondor Exp $ */
ce436d0b
MM
2
3/*
4 * Copyright (c) 2008, Matthew Mondor
5 * ALL RIGHTS RESERVED.
6 */
7
8
9#include <errno.h>
23ab503c 10#include <stdarg.h>
ce436d0b
MM
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
0ab43060 14#include <syslog.h>
ce436d0b
MM
15#include <unistd.h>
16
c20ad7db 17#include "types.h"
ce436d0b 18#include "sendq.h"
9d2ac97a 19#include "config.h"
ce436d0b
MM
20
21
ce436d0b
MM
22static bool qitem_ctor(pnode_t *);
23static void qitem_dtor(pnode_t *);
24
25
26static pool_t qitem_pool, qnode_pool;
27
28
29bool
30sendq_init(void)
31{
32
33 if (!pool_init(&qitem_pool, "qitem_pool", malloc, free,
baaaf36f 34 qitem_ctor, qitem_dtor, sizeof(qitem_t), 4096 / sizeof(qitem_t),
ce436d0b
MM
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
44qitem_t *
45qitem_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".
23ab503c 58 * The image data is expected to be placed at qi->image.
ce436d0b
MM
59 */
60void
99a016b2 61qitem_frame_init(qitem_t *qi, unsigned int size, const char *type)
ce436d0b
MM
62{
63 static const char header1[] =
23ab503c 64 "HTTP/1.1 200 OK\r\n"
9d2ac97a 65 "Server: " SERVER_VERSION "\r\n"
23ab503c
MM
66 "Connection: close\r\n"
67 "Cache-Control: no-cache\r\n"
68 "Content-Type: multipart/x-mixed-replace; boundary=--frameboundary\r\n";
ce436d0b
MM
69 char buf[QITEM_HSIZE];
70 int hlen;
71
72 hlen = snprintf(buf, QITEM_HSIZE - 1,
73 "%s"
74 "\r\n"
23ab503c
MM
75 "--frameboundary\r\n"
76 "Content-Length: %u\r\n"
77 "Content-Type: %s\r\n"
ce436d0b
MM
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
e583c3d8 87void
ce436d0b
MM
88qitem_destroy(qitem_t *qi)
89{
90
91 (void) pool_free((pnode_t *)qi);
92}
93
94static bool
95qitem_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
106static void
107qitem_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.
23ab503c
MM
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.
ce436d0b
MM
120 */
121qnode_t *
122qnode_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++;
23ab503c
MM
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 }
ce436d0b
MM
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 */
149void
150qnode_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
23ab503c
MM
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.
ce436d0b
MM
165 * Frees any flushed data, but keeps unflushed and incompletely flushed
166 * entries.
167 */
168int
169sendq_flush(int fd, list_t *q)
170{
171 qnode_t *qn, *next;
172 size_t len, wlen;
23ab503c 173 bool cl = false;
ce436d0b 174
23ab503c 175 for (qn = (qnode_t *)DLIST_TOP(q); qn != NULL && !cl; qn = next) {
ce436d0b
MM
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
23ab503c
MM
190 if (qn->close)
191 cl = true;
ce436d0b
MM
192 DLIST_UNLINK(q, (node_t *)qn);
193 qnode_destroy(qn);
194 }
195
23ab503c 196 return (cl ? ECONNRESET : 0);
ce436d0b
MM
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 */
206void
207sendq_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}
23ab503c
MM
226
227/*
228 * Utility function to queue custom data to a sendq.
229 * At most QITEM_SIZE bytes may be queued.
230 */
231bool
232sendq_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}