Commit 9bb22da6 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Writing commands and reading replies for new context

parent a52925d5
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <stdlib.h>
#include <errno.h>
/* local */ /* local */
#include "context.h" #include "context.h"
#include "format.h"
#include "object.h" #include "object.h"
static void redis__clear_address(redis_context *ctx) { static void redis__clear_address(redis_context *ctx) {
...@@ -119,3 +122,155 @@ int redis_context_connect_gai(redis_context *ctx, const char *host, int port) { ...@@ -119,3 +122,155 @@ int redis_context_connect_gai(redis_context *ctx, const char *host, int port) {
return REDIS_OK; return REDIS_OK;
} }
int redis_context_flush(redis_context *ctx) {
int rv;
int drained = 0;
while (!drained) {
rv = redis_handle_write_from_buffer(&ctx->handle, &drained);
if (rv != REDIS_OK) {
return rv;
}
}
return REDIS_OK;
}
int redis_context_read(redis_context *ctx, redis_protocol **reply) {
int rv;
redis_protocol *aux = NULL;
rv = redis_handle_read_from_buffer(&ctx->handle, &aux);
if (rv != REDIS_OK) {
return rv;
}
/* No error, no reply: make sure write buffers are flushed */
if (aux == NULL) {
rv = redis_context_flush(ctx);
if (rv != REDIS_OK) {
return rv;
}
}
while (aux == NULL) {
rv = redis_handle_wait_readable(&ctx->handle);
if (rv != REDIS_OK) {
return rv;
}
rv = redis_handle_read_to_buffer(&ctx->handle);
if (rv != REDIS_OK) {
return rv;
}
rv = redis_handle_read_from_buffer(&ctx->handle, &aux);
if (rv != REDIS_OK) {
return rv;
}
}
assert(aux != NULL);
*reply = aux;
return REDIS_OK;
}
int redis_context_write_vcommand(redis_context *ctx,
const char *format,
va_list ap)
{
char *buf;
int len;
int rv;
len = redis_format_vcommand(&buf, format, ap);
if (len < 0) {
errno = ENOMEM;
return REDIS_ESYS;
}
/* Write command to output buffer in handle */
rv = redis_handle_write_to_buffer(&ctx->handle, buf, len);
free(buf);
return rv;
}
int redis_context_write_command(redis_context *ctx,
const char *format,
...)
{
va_list ap;
int rv;
va_start(ap, format);
rv = redis_context_write_vcommand(ctx, format, ap);
va_end(ap);
return rv;
}
int redis_context_write_command_argv(redis_context *ctx,
int argc,
const char **argv,
const size_t *argvlen)
{
char *buf;
int len;
int rv;
len = redis_format_command_argv(&buf, argc, argv, argvlen);
if (len < 0) {
errno = ENOMEM;
return REDIS_ESYS;
}
/* Write command to output buffer in handle */
rv = redis_handle_write_to_buffer(&ctx->handle, buf, len);
free(buf);
return rv;
}
int redis_context_call_vcommand(redis_context *ctx,
redis_protocol **reply,
const char *format,
va_list ap)
{
int rv;
rv = redis_context_write_vcommand(ctx, format, ap);
if (rv != REDIS_OK) {
return rv;
}
return redis_context_read(ctx, reply);
}
int redis_context_call_command(redis_context *ctx,
redis_protocol **reply,
const char *format,
...)
{
va_list ap;
int rv;
va_start(ap, format);
rv = redis_context_call_vcommand(ctx, reply, format, ap);
va_end(ap);
return rv;
}
int redis_context_call_command_argv(redis_context *ctx,
redis_protocol **reply,
int argc,
const char **argv,
const size_t *argvlen)
{
int rv;
rv = redis_context_write_command_argv(ctx, argc, argv, argvlen);
if (rv != REDIS_OK) {
return rv;
}
return redis_context_read(ctx, reply);
}
#ifndef _HIREDIS_CONTEXT_H #ifndef _HIREDIS_CONTEXT_H
#define _HIREDIS_CONTEXT_H 1 #define _HIREDIS_CONTEXT_H 1
#include <stdarg.h>
/* local */ /* local */
#include "handle.h" #include "handle.h"
...@@ -24,4 +26,32 @@ int redis_context_connect_in6(redis_context *, struct sockaddr_in6 addr); ...@@ -24,4 +26,32 @@ int redis_context_connect_in6(redis_context *, struct sockaddr_in6 addr);
int redis_context_connect_un(redis_context *, struct sockaddr_un addr); int redis_context_connect_un(redis_context *, struct sockaddr_un addr);
int redis_context_connect_gai(redis_context *, const char *addr, int port); int redis_context_connect_gai(redis_context *, const char *addr, int port);
int redis_context_flush(redis_context *ctx);
int redis_context_read(redis_context *ctx, redis_protocol **reply);
int redis_context_write_vcommand(redis_context *ctx,
const char *format,
va_list ap);
int redis_context_write_command(redis_context *ctx,
const char *format,
...);
int redis_context_write_command_argv(redis_context *ctx,
int argc,
const char **argv,
const size_t *argvlen);
int redis_context_call_vcommand(redis_context *ctx,
redis_protocol **reply,
const char *format,
va_list ap);
int redis_context_call_command(redis_context *ctx,
redis_protocol **reply,
const char *format,
...);
int redis_context_call_command_argv(redis_context *ctx,
redis_protocol **reply,
int argc,
const char **argv,
const size_t *argvlen);
#endif #endif
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
/* local */ /* local */
#include "../context.h" #include "../context.h"
#include "../object.h"
#include "test-helper.h" #include "test-helper.h"
#define SETUP_CONNECT() \ #define SETUP_CONNECT() \
...@@ -165,6 +166,109 @@ TEST(connect_gai_success) { ...@@ -165,6 +166,109 @@ TEST(connect_gai_success) {
redis_context_destroy(&c); redis_context_destroy(&c);
} }
#define SETUP_CONNECTED() \
SETUP_CONNECT(); \
rv = redis_context_connect_gai(&c, "localhost", redis_port()); \
assert_equal_int(rv, REDIS_OK);
static redis_object *read(redis_context *ctx) {
redis_protocol *reply;
int rv;
rv = redis_context_read(ctx, &reply);
assert_equal_return(rv, REDIS_OK);
return (redis_object*)reply->data;
}
static void compare_ok_status(redis_object *obj) {
assert_equal_int(obj->type, REDIS_STATUS);
assert_equal_string(obj->str, "OK");
assert_equal_int(obj->len, 2);
}
static void compare_string(redis_object *obj, const char *str, size_t len) {
assert_equal_int(obj->type, REDIS_STRING);
assert_equal_string(obj->str, str);
assert_equal_int(obj->len, len);
}
TEST(write_command) {
SETUP_CONNECTED();
rv = redis_context_write_command(&c, "set foo bar");
assert_equal_int(rv, REDIS_OK);
compare_ok_status(read(&c));
/* Check that the command was executed as intended */
rv = redis_context_write_command(&c, "get foo");
assert_equal_int(rv, REDIS_OK);
compare_string(read(&c), "bar", 3);
redis_context_destroy(&c);
}
TEST(write_command_argv) {
SETUP_CONNECTED();
int argc1 = 3;
const char *argv1[] = { "set", "foo", "bar" };
size_t argvlen1[] = { 3, 3, 3 };
rv = redis_context_write_command_argv(&c, argc1, argv1, argvlen1);
assert_equal_int(rv, REDIS_OK);
compare_ok_status(read(&c));
/* Check that the command was executed as intended */
int argc2 = 2;
const char *argv2[] = { "get", argv1[1] };
size_t argvlen2[] = { 3, argvlen1[1] };
rv = redis_context_write_command_argv(&c, argc2, argv2, argvlen2);
assert_equal_int(rv, REDIS_OK);
compare_string(read(&c), argv1[2], argvlen1[2]);
redis_context_destroy(&c);
}
TEST(call_command) {
SETUP_CONNECTED();
redis_protocol *reply;
rv = redis_context_call_command(&c, &reply, "set foo bar");
assert_equal_int(rv, REDIS_OK);
compare_ok_status(reply->data);
/* Check that the command was executed as intended */
rv = redis_context_call_command(&c, &reply, "get foo");
assert_equal_int(rv, REDIS_OK);
compare_string(reply->data, "bar", 3);
redis_context_destroy(&c);
}
TEST(call_command_argv) {
SETUP_CONNECTED();
redis_protocol *reply;
int argc1 = 3;
const char *argv1[] = { "set", "foo", "bar" };
size_t argvlen1[] = { 3, 3, 3 };
rv = redis_context_call_command_argv(&c, &reply, argc1, argv1, argvlen1);
assert_equal_int(rv, REDIS_OK);
compare_ok_status(reply->data);
/* Check that the command was executed as intended */
int argc2 = 2;
const char *argv2[] = { "get", argv1[1] };
size_t argvlen2[] = { 3, argvlen1[1] };
rv = redis_context_call_command_argv(&c, &reply, argc2, argv2, argvlen2);
assert_equal_int(rv, REDIS_OK);
compare_string(reply->data, argv1[2], argvlen1[2]);
redis_context_destroy(&c);
}
int main(void) { int main(void) {
test_connect_in_refused(); test_connect_in_refused();
test_connect_in6_refused(); test_connect_in6_refused();
...@@ -174,4 +278,9 @@ int main(void) { ...@@ -174,4 +278,9 @@ int main(void) {
test_connect_gai_unknown_host(); test_connect_gai_unknown_host();
test_connect_gai_timeout(); test_connect_gai_timeout();
test_connect_gai_success(); test_connect_gai_success();
test_write_command();
test_write_command_argv();
test_call_command();
test_call_command_argv();
} }
...@@ -6,6 +6,15 @@ ...@@ -6,6 +6,15 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/time.h> #include <sys/time.h>
#include <assert.h> #include <assert.h>
#include <errno.h>
/* gai_strerror */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
/* local */
#include "../handle.h"
/******************************************************************************/ /******************************************************************************/
/* ASSERTS ********************************************************************/ /* ASSERTS ********************************************************************/
...@@ -48,6 +57,42 @@ ...@@ -48,6 +57,42 @@
} \ } \
} while(0) } while(0)
#define ERR_TO_STR_OFFSET 4
static const char *err_to_str[] = {
[REDIS_OK + ERR_TO_STR_OFFSET] = "REDIS_OK",
[REDIS_ESYS + ERR_TO_STR_OFFSET] = "REDIS_ESYS",
[REDIS_EGAI + ERR_TO_STR_OFFSET] = "REDIS_EGAI",
[REDIS_EPARSER + ERR_TO_STR_OFFSET] = "REDIS_EPARSER",
[REDIS_EEOF + ERR_TO_STR_OFFSET] = "REDIS_EEOF"
};
static void assert_equal_return_failure(int actual, int expected, const char *file, int line) {
const char *actualstr = err_to_str[actual + ERR_TO_STR_OFFSET];
const char *expectedstr = err_to_str[expected + ERR_TO_STR_OFFSET];
char reason[128];
switch (actual) {
case REDIS_ESYS:
sprintf(reason, "%d:%s", errno, strerror(errno));
break;
case REDIS_EGAI:
sprintf(reason, "%d:%s", errno, gai_strerror(errno));
break;
}
fprintf(stderr, "%s:%d: ", file, line);
fprintf(stderr, "%s != %s (%s)\n", actualstr, expectedstr, reason);
}
#define assert_equal_return(a, b) do { \
if ((a) != (b)) { \
assert_equal_return_failure( \
(a), (b), \
__FILE__, __LINE__); \
assert(0); \
} \
} while(0)
/******************************************************************************/ /******************************************************************************/
/* TEST DEFINITION ************************************************************/ /* TEST DEFINITION ************************************************************/
/******************************************************************************/ /******************************************************************************/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment