It's assumed that only valid requests are received. And that read()
doesn't block until \r\n\r\n
is received.
a.c
:
#include <unistd.h>
#include <err.h>
#include <string.h>
#include "a.h"
#define BUF_SIZE 10
/*
#include <stdio.h>
void print_buf(char *buf, size_t len)
{
fputs("buf: (", stdout);
for (size_t i = 0; i < len; i++) {
if (buf[i] < 32 || buf[i] == 127) {
printf("\\%02x", buf[i]);
} else putchar(buf[i]);
}
puts(")");
}
*/
size_t read_request(int client) {
char buf[BUF_SIZE], *bufp = buf;
size_t n_read = 0;
while (1) {
ssize_t r;
size_t i;
r = read(client, bufp, BUF_SIZE - (bufp - buf));
if (r == -1) err(1, "read()");
n_read += r;
/* print_buf(buf, bufp - buf + r); */
/* find the beginning of the last \r\n sequence */
i = r;
while (i > 0 && r - i < 4 && (bufp[i - 1] == '\r' || bufp[i - 1] == '\n'))
i--;
/* copy the sequence to the beginning of the buffer */
if (i > 0) {
i += bufp - buf;
r += bufp - buf;
bufp = buf;
}
if (i < (size_t)r) {
size_t j;
for (j = 0; i < (size_t)r; j++, i++)
bufp[j] = bufp[i];
bufp = bufp + j;
}
/* break if reached \r\n\r\n */
if (bufp - buf == 4 && memcmp(buf, "\r\n\r\n", 4) == 0)
break;
}
return n_read;
}
a.h
:
size_t read_request(int);
a.test.c
:
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include "d.h"
size_t n_executed = 0;
ssize_t __wrap_read(int, void *, size_t);
ssize_t __wrap_read_impl(int, void *, size_t, size_t, ...);
ssize_t __wrap_read(int fd, void *buf, size_t count) {
switch (fd) {
case 1: return __wrap_read_impl(fd, buf, count, 4, "\r", "\n", "\r", "\n");
case 2: return __wrap_read_impl(fd, buf, count, 2, "\r\n", "a\r\n\r\n");
case 3: return __wrap_read_impl(fd, buf, count, 2, "a", "\r\n\r\n");
default: return -1;
}
}
ssize_t __wrap_read_impl(int fd, void *buf, size_t count, size_t n, ...) {
(void) fd; /* unused */
(void) count; /* unused */
va_list args;
size_t i;
char *r;
if (n == n_executed) {
puts("blocking...");
return -1;
}
va_start(args, n);
for (i = 0; i < n_executed + 1; i++)
r = va_arg(args, char *);
va_end(args);
memcpy(buf, r, strlen(r));
n_executed++;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
return strlen(r);
#pragma GCC diagnostic pop
}
static void test_read_mock1(void **state) {
(void) state; /* unused */
size_t r;
n_executed = 0;
r = read_request(1);
assert_int_equal(r, 4);
}
static void test_read_mock2(void **state) {
(void) state; /* unused */
size_t r;
n_executed = 0;
r = read_request(2);
assert_int_equal(r, 7);
}
static void test_read_mock3(void **state) {
(void) state; /* unused */
size_t r;
n_executed = 0;
r = read_request(3);
assert_int_equal(r, 5);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_read_mock1),
cmocka_unit_test(test_read_mock2),
cmocka_unit_test(test_read_mock3),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
$ gcc -Wall -Wextra -pedantic -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -std=c99 -O \
d.c d.test.c -lcmocka -Wl,--wrap=read
$ ./a.out