Skip to content

Commit 8b86f63

Browse files
committed
Changed implementation for csv from buffer based to bucket/brigade based
1 parent d0a2161 commit 8b86f63

File tree

4 files changed

+82
-115
lines changed

4 files changed

+82
-115
lines changed

TODO

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
- Replace input/output with buckets and brigades
21
- Replace current database connection pool with apr's version
32
- Replace PostgresQL interface with apr's vendor independed version.
43
- Add cookie output support

src/csv.c

Lines changed: 75 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -20,134 +20,108 @@
2020
#include <apr_hash.h>
2121
#include "csv.h"
2222

23-
#define MAX_CSV_LINE_SIZE 256
24-
25-
int mod_okioki_buffer_append(char *buffer, size_t buffer_size, size_t *buffer_length, char *s, int s_length)
26-
{
27-
// If unknown, calculate the length of s.
28-
if (s_length == -1) {
29-
s_length = strlen(s);
30-
}
31-
32-
// Check if there is enough room in the buffer.
33-
if (*buffer_length + s_length > buffer_size) {
34-
return HTTP_INTERNAL_SERVER_ERROR;
35-
}
36-
37-
// Copy s into the buffer. We know the length, so no strcpy needed.
38-
memcpy(&buffer[*buffer_length], s, s_length);
39-
*buffer_length += s_length;
40-
return HTTP_OK;
41-
}
42-
43-
int mod_okioki_buffer_append_value(apr_pool_t *pool, char *buffer, size_t buffer_size, size_t *buffer_length, char *s, int s_length)
23+
int mod_okioki_buffer_append_value(apr_bucket_brigade *bb, apr_pool_t *pool, apr_bucket_alloc_t *alloc, char *s)
4424
{
4525
int i, j;
4626
int need_quote = 0;
47-
int need_escape = 0;
48-
char *s2;
4927
char c;
50-
int ret;
51-
52-
// If the length of s is unknown, calculate it.
53-
if (s_length == -1) {
54-
s_length = strlen(s);
55-
}
28+
int s_length = strlen(s);
29+
apr_bucket *b;
30+
apr_bucket *b_before = APR_BRIGADE_LAST(bb);
5631

57-
// PASS 1: Check the string for any characters that need to be escaped.
58-
for (i = 0; i < s_length; i++) {
32+
// Copy the string, and escape quotes.
33+
for (j = i = 0; i < s_length; i++) {
5934
c = s[i];
6035
switch (c) {
6136
case '"':
62-
need_escape++; // fall tru
37+
// Add the text, before and including this quote.
38+
HTTP_ASSERT_NOT_NULL(
39+
b = apr_bucket_transient_create(&s[j], (i - j) + 1, alloc),
40+
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Could not allocate bucket."
41+
)
42+
APR_BRIGADE_INSERT_TAIL(bb, b);
43+
44+
// Double the quote.
45+
HTTP_ASSERT_NOT_NULL(
46+
b = apr_bucket_immortal_create("\"", 1, alloc),
47+
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Could not allocate bucket."
48+
)
49+
APR_BRIGADE_INSERT_TAIL(bb, b);
50+
51+
// Set j to just after this character.
52+
j = i + 1;
6353
case '\n':
6454
case '\r':
6555
case ',':
6656
need_quote++;
6757
break;
6858
}
6959
}
70-
71-
// PASS 2: If there was quote we need to escape for each quote.
72-
if (need_escape) {
73-
// Escaping uses more space, so allocate a new string.
74-
s2 = apr_palloc(pool, s_length + need_escape + 1);
75-
76-
// Copy the string, doubling a quote and ending with a nul.
77-
for (j = i = 0; i < (s_length + 1); i++) {
78-
c = s[i];
79-
s2[j++] = c;
80-
if (c == '"') {
81-
s2[j++] = c;
82-
}
83-
}
84-
85-
// After this we are going to use s like normally.
86-
s = s2;
60+
if (j < i) {
61+
// Add the last piece of text.
62+
HTTP_ASSERT_NOT_NULL(
63+
b = apr_bucket_transient_create(&s[j], i - j, alloc),
64+
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Could not allocate bucket."
65+
)
66+
APR_BRIGADE_INSERT_TAIL(bb, b);
8767
}
8868

89-
// Add a quote if the string needed to be quoted.
9069
if (need_quote) {
91-
HTTP_ASSERT_OK(
92-
ret = mod_okioki_buffer_append(buffer, buffer_size, buffer_length, "\"", 1),
93-
ret, "[mod_okioki] Not enough room to store CSV result."
70+
// Add a quote at the end of the value.
71+
HTTP_ASSERT_NOT_NULL(
72+
b = apr_bucket_immortal_create("\"", 1, alloc),
73+
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Could not allocate bucket."
9474
)
95-
}
75+
APR_BRIGADE_INSERT_TAIL(bb, b);
9676

97-
// Add the "escaped" string.
98-
HTTP_ASSERT_OK(
99-
ret = mod_okioki_buffer_append(buffer, buffer_size, buffer_length, s, s_length + need_escape),
100-
ret, "[mod_okioki] Not enough room to store CSV result."
101-
)
102-
103-
// Add an other quote if the string needed to be quoted.
104-
if (need_quote) {
105-
HTTP_ASSERT_OK(
106-
ret = mod_okioki_buffer_append(buffer, buffer_size, buffer_length, "\"", 1),
107-
ret, "[mod_okioki] Not enough room to store CSV result."
77+
// Also add a quote before the value.
78+
HTTP_ASSERT_NOT_NULL(
79+
b = apr_bucket_immortal_create("\"", 1, alloc),
80+
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Could not allocate bucket."
10881
)
82+
APR_BUCKET_INSERT_AFTER(b_before, b);
10983
}
11084

11185
return HTTP_OK;
11286
}
11387

114-
char *mod_okioki_generate_csv(apr_pool_t *pool, view_t *view, apr_array_header_t *result, size_t *result_s_len)
88+
apr_bucket_brigade *mod_okioki_generate_csv(apr_pool_t *pool, apr_bucket_alloc_t *alloc, view_t *view, apr_array_header_t *result)
11589
{
11690
int i, j;
11791
char *value;
11892
apr_hash_t *row;
93+
apr_bucket_brigade *bb;
94+
apr_bucket *b;
11995

120-
size_t buffer_size = (result->nelts + 1) * MAX_CSV_LINE_SIZE;
121-
size_t buffer_length = 0;
122-
char *buffer = apr_palloc(pool, buffer_size);
123-
124-
*result_s_len = 0;
125-
126-
if (result == NULL || result->nelts == 0) {
127-
return "";
128-
}
96+
HTTP_ASSERT_NOT_NULL(
97+
bb = apr_brigade_create(pool, alloc),
98+
NULL, "[mod_okioki] Could not allocate a bucket brigade."
99+
)
129100

130101
// Create a csv header.
131102
for (i = 0; i < view->nr_csv_params; i++) {
132103
// Add a comma between each entry.
133104
if (i != 0) {
134-
HTTP_ASSERT_OK(
135-
mod_okioki_buffer_append(buffer, buffer_size, &buffer_length, ",", 1),
136-
NULL, "[mod_okioki] Not enough room to store CSV result."
105+
HTTP_ASSERT_NOT_NULL(
106+
b = apr_bucket_immortal_create(",", 1, alloc),
107+
NULL, "[mod_okioki] Could not allocate bucket."
137108
)
109+
APR_BRIGADE_INSERT_TAIL(bb, b);
138110
}
139111

140112
// Copy the name of the column.
141-
HTTP_ASSERT_OK(
142-
mod_okioki_buffer_append_value(pool, buffer, buffer_size, &buffer_length, view->csv_params[i], view->csv_params_len[i]),
143-
NULL, "[mod_okioki] Not enough room to store CSV result."
113+
HTTP_ASSERT_NOT_NULL(
114+
b = apr_bucket_immortal_create(view->csv_params[i], view->csv_params_len[i], alloc),
115+
NULL, "[mod_okioki] Could not allocate bucket."
144116
)
117+
APR_BRIGADE_INSERT_TAIL(bb, b);
145118
}
146119
// Finish off the header with a carriage return and linefeed.
147-
HTTP_ASSERT_OK(
148-
mod_okioki_buffer_append(buffer, buffer_size, &buffer_length, "\r\n", 2),
149-
NULL, "[mod_okioki] Not enough room to store CSV result."
120+
HTTP_ASSERT_NOT_NULL(
121+
b = apr_bucket_immortal_create("\r\n", 2, alloc),
122+
NULL, "[mod_okioki] Could not allocate bucket."
150123
)
124+
APR_BRIGADE_INSERT_TAIL(bb, b);
151125

152126
// Check each row and figure out all the column names.
153127
for (j = 0; j < result->nelts; j++) {
@@ -160,32 +134,39 @@ char *mod_okioki_generate_csv(apr_pool_t *pool, view_t *view, apr_array_header_t
160134
for (i = 0; i < view->nr_csv_params; i++) {
161135
// Add a comma between each entry.
162136
if (i != 0) {
163-
HTTP_ASSERT_OK(
164-
mod_okioki_buffer_append(buffer, buffer_size, &buffer_length, ",", 1),
165-
NULL, "[mod_okioki] Not enough room to store CSV result."
137+
HTTP_ASSERT_NOT_NULL(
138+
b = apr_bucket_immortal_create(",", 1, alloc),
139+
NULL, "[mod_okioki] Could not allocate bucket."
166140
)
141+
APR_BRIGADE_INSERT_TAIL(bb, b);
167142
}
168143

169144
HTTP_ASSERT_NOT_NULL(
170145
value = apr_hash_get(row, view->csv_params[i], view->csv_params_len[i]),
171146
NULL, "[mod_okioki] column '%s' not found in result.", view->csv_params[i]
172147
)
173148

174-
// Copy the name of the column.
149+
// Copy the value of the column.
175150
HTTP_ASSERT_OK(
176-
mod_okioki_buffer_append_value(pool, buffer, buffer_size, &buffer_length, value, -1),
151+
mod_okioki_buffer_append_value(bb, pool, alloc, value),
177152
NULL, "[mod_okioki] Not enough room to store CSV result."
178153
)
179154
}
180155

181156
// Finish off the header with a carriage return and linefeed.
182-
HTTP_ASSERT_OK(
183-
mod_okioki_buffer_append(buffer, buffer_size, &buffer_length, "\r\n", 2),
184-
NULL, "[mod_okioki] Not enough room to store CSV result."
157+
HTTP_ASSERT_NOT_NULL(
158+
b = apr_bucket_immortal_create("\r\n", 2, alloc),
159+
NULL, "[mod_okioki] Could not allocate bucket."
185160
)
161+
APR_BRIGADE_INSERT_TAIL(bb, b);
186162
}
187163

188-
*result_s_len = buffer_length;
189-
return buffer;
164+
// Add an end-of-stream.
165+
HTTP_ASSERT_NOT_NULL(
166+
b = apr_bucket_eos_create(alloc),
167+
NULL, "[mod_okioki] Could not allocate bucket."
168+
)
169+
APR_BRIGADE_INSERT_TAIL(bb, b);
170+
return bb;
190171
}
191172

src/csv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@
2222
#include <apr_tables.h>
2323
#include "mod_okioki.h"
2424

25-
char *mod_okioki_generate_csv(apr_pool_t *pool, view_t *view, apr_array_header_t *result, size_t *result_s_len);
25+
apr_bucket_brigade *mod_okioki_generate_csv(apr_pool_t *pool, apr_bucket_alloc_t *alloc, view_t *view, apr_array_header_t *result);
2626

2727
#endif

src/mod_okioki.c

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ static void *mod_okioki_create_dir_config(apr_pool_t *pool, char *dir)
7575
static int mod_okioki_handler(request_rec *http_request)
7676
{
7777
apr_pool_t *pool = http_request->pool;
78+
apr_bucket_alloc_t *alloc = http_request->connection->bucket_alloc;
7879
mod_okioki_dir_config *cfg;
7980
view_t *view;
8081
apr_hash_t *arguments;
8182
apr_array_header_t *result;
82-
char *result_s;
83-
size_t result_s_len;
83+
apr_bucket_brigade *bb_out;
8484
int ret;
8585

8686
// Check if we need to process the request. We only need to if the handler is set to "okioki-handler".
@@ -138,26 +138,13 @@ static int mod_okioki_handler(request_rec *http_request)
138138

139139
// Convert result to csv string.
140140
HTTP_ASSERT_NOT_NULL(
141-
result_s = mod_okioki_generate_csv(pool, view, result, &result_s_len),
141+
bb_out = mod_okioki_generate_csv(pool, alloc, view, result),
142142
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Could not generate text/csv."
143143
)
144144

145-
if (result_s_len > 0) {
146-
ap_set_content_type(http_request, "text/csv");
147-
ap_set_content_length(http_request, result_s_len);
148-
149-
HTTP_ASSERT_NOT_NEG(
150-
ap_rwrite(result_s, result_s_len, http_request),
151-
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Write failed, client closed connection."
152-
)
153-
154-
HTTP_ASSERT_NOT_NEG(
155-
ap_rflush(http_request),
156-
HTTP_INTERNAL_SERVER_ERROR, "[mod_okioki] Write failed, client closed connection."
157-
)
158-
}
159-
160-
return HTTP_OK;
145+
ap_set_content_type(http_request, "text/csv");
146+
147+
return ap_pass_brigade(http_request->output_filters, bb_out);
161148
}
162149

163150
/** This function setups all the handlers at startup.

0 commit comments

Comments
 (0)