*** empty log message ***
[mmondor.git] / tests / bktr_httpd / sendq.c
CommitLineData
ce436d0b
MM
1/* $Id: sendq.c,v 1.1 2008/01/19 00:29:02 mmondor Exp $ */
2
3/*
4 * Copyright (c) 2008, Matthew Mondor
5 * ALL RIGHTS RESERVED.
6 */
7
8
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include "sendq.h"
16
17
18static void qitem_destroy(qitem_t *);
19static bool qitem_ctor(pnode_t *);
20static void qitem_dtor(pnode_t *);
21
22
23static pool_t qitem_pool, qnode_pool;
24
25
26bool
27sendq_init(void)
28{
29
30 if (!pool_init(&qitem_pool, "qitem_pool", malloc, free,
31 qitem_ctor, qitem_dtor, sizeof(qitem_t), 65536 / sizeof(qitem_t),
32 1, 0))
33 return false;
34 if (!pool_init(&qnode_pool, "qnode_pool", malloc, free,
35 NULL, NULL, sizeof(qnode_t), 65536 / sizeof(qnode_t), 1, 0))
36 return false;
37
38 return true;
39}
40
41qitem_t *
42qitem_new(void)
43{
44 qitem_t *qi;
45
46 if ((qi = (qitem_t *)pool_alloc(&qitem_pool, false)) != NULL)
47 qi->refcnt = 0;
48
49 return qi;
50}
51
52/*
53 * Initializes the qitem headers. <size> should be the size of the image in
54 * bytes, while <type> should be a mime type, such as "image/jpeg".
55 */
56void
57qitem_init(qitem_t *qi, size_t size, const char *type)
58{
59 static const char header1[] =
60 "HTTP/1.0 200 OK\r\n"
61 "Server: bktr_httpd 0.1/mmondor\r\n"
62 "Content-type: multipart/x-mixed-replace; boundary=--myboundary\r\n";
63 char buf[QITEM_HSIZE];
64 int hlen;
65
66 hlen = snprintf(buf, QITEM_HSIZE - 1,
67 "%s"
68 "\r\n"
69 "--myboundary\r\n"
70 "Content-length: %u\r\n"
71 "Content-type: %s\r\n"
72 "\r\n",
73 header1, size, type);
74
75 qi->header1 = &qi->image[-hlen];
76 qi->header2 = &qi->header1[sizeof(header1)];
77 (void) memcpy(qi->header1, buf, hlen);
78 qi->end = &qi->image[size];
79}
80
81static void
82qitem_destroy(qitem_t *qi)
83{
84
85 (void) pool_free((pnode_t *)qi);
86}
87
88static bool
89qitem_ctor(pnode_t *pn)
90{
91 qitem_t *qi = (qitem_t *)pn;
92
93 if ((qi->data = malloc(QITEM_SIZE)) == NULL)
94 return false;
95 qi->image = &qi->data[QITEM_HSIZE];
96
97 return true;
98}
99
100static void
101qitem_dtor(pnode_t *pn)
102{
103 qitem_t *qi = (qitem_t *)pn;
104
105 free(qi->data);
106}
107
108/*
109 * Creates a new sendq node from the shared qitem, and sets it up to begin at
110 * either qi->header1 or qi->header2, depending on if it's the first node
111 * queued for it or if it's a subsequent one.
112 */
113qnode_t *
114qnode_new(qitem_t *qi, bool *first)
115{
116 qnode_t *qn;
117
118 if ((qn = (qnode_t *)pool_alloc(&qnode_pool, false)) != NULL) {
119 qn->qitem = qi;
120 qi->refcnt++;
121 if (*first) {
122 qn->ptr = qi->header1;
123 *first = false;
124 } else
125 qn->ptr = qi->header2;
126 }
127
128 return qn;
129}
130
131/*
132 * Destroys this sendq node, and automatically destroy the wrapped shared
133 * qitem if no more qnodes reference to it.
134 */
135void
136qnode_destroy(qnode_t *qn)
137{
138
139 if (--qn->qitem->refcnt == 0)
140 qitem_destroy(qn->qitem);
141
142 (void) pool_free((pnode_t *)qn);
143}
144
145/*
146 * Attempts to flush the queue. Returns 0 if the queue could be completely
147 * flushed, EAGAIN if it only could partially be sent, or EPIPE on error.
148 * EAGAIN means that the client cannot read fast enough.
149 * Frees any flushed data, but keeps unflushed and incompletely flushed
150 * entries.
151 */
152int
153sendq_flush(int fd, list_t *q)
154{
155 qnode_t *qn, *next;
156 size_t len, wlen;
157
158 for (qn = (qnode_t *)DLIST_TOP(q); qn != NULL; qn = next) {
159 next = (qnode_t *)DLIST_NEXT((node_t *)qn);
160
161 len = qn->qitem->end - qn->ptr;
162 wlen = write(fd, qn->ptr, qn->qitem->end - qn->ptr);
163 if (wlen == -1) {
164 if (errno == EAGAIN)
165 return EAGAIN;
166 return EPIPE;
167 }
168 if (wlen < len) {
169 qn->ptr += wlen;
170 return EAGAIN;
171 }
172
173 DLIST_UNLINK(q, (node_t *)qn);
174 qnode_destroy(qn);
175 }
176
177 return 0;
178}
179
180/*
181 * Purges the sendq buffers. If <all> is true, every entry is destroyed.
182 * Otherwise, the first entry is preserved (assumed to be incompletely sent)
183 * and all other entries discarded. Useful to call before closing connection
184 * with a client or when sendq_flush() returns EAGAIN, in which case the
185 * client is not able to read() fast enough.
186 */
187void
188sendq_purge(list_t *q, bool all)
189{
190 qnode_t *qn, *next;
191
192 if (!all) {
193 /* Skip first entry if any */
194 if ((qn = (qnode_t *)DLIST_TOP(q)) != NULL)
195 qn = (qnode_t *)DLIST_NEXT((node_t *)qn);
196 } else
197 qn = (qnode_t *)DLIST_TOP(q);
198
199 /* Purge any remaining entries */
200 for ( ; qn != NULL; qn = next) {
201 next = (qnode_t *)DLIST_NEXT((node_t *)qn);
202
203 DLIST_UNLINK(q, (node_t *)qn);
204 qnode_destroy(qn);
205 }
206}