Commit a52925d5 authored by Pieter Noordhuis's avatar Pieter Noordhuis
Browse files

Initial stab at a new context structure

parent 11ad647a
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
include ./Makefile.common include ./Makefile.common
OBJ=net.o hiredis.o sds.o async.o parser.o object.o handle.o format.o OBJ=net.o hiredis.o sds.o async.o parser.o object.o handle.o format.o context.o
BINS=hiredis-example hiredis-test BINS=hiredis-example hiredis-test
all: $(DYLIBNAME) $(BINS) all: $(DYLIBNAME) $(BINS)
......
#include <string.h>
#include <assert.h>
/* local */
#include "context.h"
#include "object.h"
static void redis__clear_address(redis_context *ctx) {
memset(&ctx->address, 0, sizeof(ctx->address));
}
int redis_context_init(redis_context *ctx) {
int rv;
rv = redis_handle_init(&ctx->handle);
if (rv != REDIS_OK) {
return rv;
}
/* Pull default timeout from handle */
ctx->timeout = redis_handle_get_timeout(&ctx->handle);
/* Reinitialize parser with bundled object functions */
redis_parser_init(&ctx->handle.parser, &redis_object_parser_callbacks);
/* Clear address field */
redis__clear_address(ctx);
return REDIS_OK;
}
int redis_context_destroy(redis_context *ctx) {
int rv;
rv = redis_handle_destroy(&ctx->handle);
return rv;
}
int redis_context_set_timeout(redis_context *ctx, unsigned long timeout) {
int rv;
rv = redis_handle_set_timeout(&ctx->handle, timeout);
if (rv == REDIS_OK) {
ctx->timeout = timeout;
}
return rv;
}
unsigned long redis_context_get_timeout(redis_context *ctx) {
return ctx->timeout;
}
static int redis__connect(redis_context *ctx) {
int rv;
rv = redis_handle_connect_address(&ctx->handle, &ctx->address);
if (rv != REDIS_OK) {
return rv;
}
rv = redis_handle_wait_connected(&ctx->handle);
if (rv != REDIS_OK) {
return rv;
}
return REDIS_OK;
}
static int redis__first_connect(redis_context *ctx) {
int rv;
rv = redis__connect(ctx);
if (rv != REDIS_OK) {
redis__clear_address(ctx);
return rv;
}
return REDIS_OK;
}
int redis_context_connect_in(redis_context *ctx, struct sockaddr_in addr) {
ctx->address.sa_family = AF_INET;
ctx->address.sa_addrlen = sizeof(addr);
ctx->address.sa_addr.in = addr;
return redis__first_connect(ctx);
}
int redis_context_connect_in6(redis_context *ctx, struct sockaddr_in6 addr) {
ctx->address.sa_family = AF_INET6;
ctx->address.sa_addrlen = sizeof(addr);
ctx->address.sa_addr.in6 = addr;
return redis__first_connect(ctx);
}
int redis_context_connect_un(redis_context *ctx, struct sockaddr_un addr) {
ctx->address.sa_family = AF_LOCAL;
ctx->address.sa_addrlen = sizeof(addr);
ctx->address.sa_addr.un = addr;
return redis__first_connect(ctx);
}
int redis_context_connect_gai(redis_context *ctx, const char *host, int port) {
redis_address address;
int rv;
rv = redis_handle_connect_gai(&ctx->handle, AF_INET, host, port, &address);
if (rv != REDIS_OK) {
return rv;
}
rv = redis_handle_wait_connected(&ctx->handle);
if (rv != REDIS_OK) {
return rv;
}
/* Store address that getaddrinfo resolved and we are now connected to */
ctx->address = address;
return REDIS_OK;
}
#ifndef _HIREDIS_CONTEXT_H
#define _HIREDIS_CONTEXT_H 1
/* local */
#include "handle.h"
typedef struct redis_context_s redis_context;
struct redis_context_s {
redis_handle handle;
unsigned long timeout;
/* private: used to reconnect */
redis_address address;
};
int redis_context_init(redis_context *ctx);
int redis_context_destroy(redis_context *ctx);
int redis_context_set_timeout(redis_context *, unsigned long us);
unsigned long redis_handle_get_timeout(redis_handle *);
int redis_context_connect_in(redis_context *, struct sockaddr_in addr);
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_gai(redis_context *, const char *addr, int port);
#endif
include ../Makefile.common include ../Makefile.common
TESTS=test-format test-parser test-object test-handle TESTS=test-format test-parser test-object test-handle test-context
all: $(TESTS) all: $(TESTS)
......
#include "../fmacros.h"
/* misc */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
/* inet_pton */
#include <arpa/inet.h>
/* getaddrinfo */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
/* local */
#include "../context.h"
#include "test-helper.h"
#define SETUP_CONNECT() \
redis_context c; \
int rv; \
rv = redis_context_init(&c); \
assert(rv == REDIS_OK); \
rv = redis_context_set_timeout(&c, 10000); \
assert(rv == REDIS_OK);
TEST(connect_in_refused) {
SETUP_CONNECT();
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(redis_port() + 1);
assert(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) == 1);
rv = redis_context_connect_in(&c, sa);
assert_equal_int(rv, REDIS_ESYS);
assert_equal_int(errno, ECONNREFUSED);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_in6_refused) {
SETUP_CONNECT();
struct sockaddr_in6 sa;
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(redis_port() + 1);
assert(inet_pton(AF_INET6, "::1", &sa.sin6_addr) == 1);
rv = redis_context_connect_in6(&c, sa);
assert_equal_int(rv, REDIS_ESYS);
assert_equal_int(errno, ECONNREFUSED);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_un_noent) {
SETUP_CONNECT();
struct sockaddr_un sa;
sa.sun_family = AF_LOCAL;
strcpy((char*)&sa.sun_path, "/tmp/idontexist.sock");
rv = redis_context_connect_un(&c, sa);
assert_equal_int(rv, REDIS_ESYS);
assert_equal_int(errno, ENOENT);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_timeout) {
SETUP_CONNECT();
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(redis_port());
assert(inet_pton(AF_INET, "10.255.255.254", &sa.sin_addr) == 1);
long long t1, t2;
t1 = usec();
rv = redis_context_connect_in(&c, sa);
t2 = usec();
assert_equal_int(rv, REDIS_ESYS);
assert_equal_int(errno, ETIMEDOUT);
assert((t2 - t1) < 15000); /* 5ms of slack should be enough */
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_success) {
SETUP_CONNECT();
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(redis_port());
assert(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr) == 1);
rv = redis_context_connect_in(&c, sa);
assert_equal_int(rv, REDIS_OK);
/* Address should be set when connection was made */
assert_equal_int(c.address.sa_family, AF_INET);
redis_context_destroy(&c);
}
TEST(connect_gai_unknown_host) {
SETUP_CONNECT();
rv = redis_context_connect_gai(&c, "idontexist.foo", redis_port());
assert_equal_int(rv, REDIS_EGAI);
/* Don't care about the specific error for now. */
assert(1);
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_gai_timeout) {
SETUP_CONNECT();
long long t1, t2;
t1 = usec();
rv = redis_context_connect_gai(&c, "10.255.255.254", redis_port());
t2 = usec();
assert_equal_int(rv, REDIS_ESYS);
assert_equal_int(errno, ETIMEDOUT);
assert((t2 - t1) < 15000); /* 5ms of slack should be enough */
/* Address shouldn't be set when no connection could be made */
assert_equal_int(c.address.sa_family, 0);
redis_context_destroy(&c);
}
TEST(connect_gai_success) {
SETUP_CONNECT();
rv = redis_context_connect_gai(&c, "localhost", redis_port());
assert_equal_int(rv, REDIS_OK);
/* Address should be set when connection was made */
assert_equal_int(c.address.sa_family, AF_INET);
redis_context_destroy(&c);
}
int main(void) {
test_connect_in_refused();
test_connect_in6_refused();
test_connect_un_noent();
test_connect_timeout();
test_connect_success();
test_connect_gai_unknown_host();
test_connect_gai_timeout();
test_connect_gai_success();
}
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