Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
hiredis
Commits
aacf12c2
Commit
aacf12c2
authored
Oct 01, 2011
by
Pieter Noordhuis
Browse files
Bootstrap redis_handle
parent
6ccf65d5
Changes
5
Hide whitespace changes
Inline
Side-by-side
Makefile
View file @
aacf12c2
...
...
@@ -3,7 +3,7 @@
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
# This file is released under the BSD license, see the COPYING file
OBJ
=
net.o hiredis.o sds.o async.o parser.o object.o
OBJ
=
net.o hiredis.o sds.o async.o parser.o object.o
handle.o
BINS
=
hiredis-example hiredis-test
LIBNAME
=
libhiredis
...
...
handle.c
0 → 100644
View file @
aacf12c2
#include "fmacros.h"
/* misc */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
/* socket/connect/(get|set)sockopt*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
/* TCP_* constants */
/* fcntl */
#include <unistd.h>
#include <fcntl.h>
/* select */
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
/* getaddrinfo */
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
/* local */
#include "handle.h"
#define REDIS__READABLE 1
#define REDIS__WRITABLE 2
int
redis_handle_init
(
redis_handle
*
h
)
{
h
->
fd
=
-
1
;
h
->
timeout
.
tv_sec
=
5
;
h
->
timeout
.
tv_usec
=
0
;
redis_parser_init
(
&
h
->
parser
,
NULL
);
return
REDIS_OK
;
}
/* Associate timeout with handle. Only used in redis_handle_wait_* calls. */
int
redis_handle_set_timeout
(
redis_handle
*
h
,
unsigned
long
us
)
{
struct
timeval
to
;
to
.
tv_sec
=
us
/
1000000
;
to
.
tv_usec
=
us
-
(
1000000
*
to
.
tv_sec
);
h
->
timeout
=
to
;
return
REDIS_OK
;
}
int
redis_handle_close
(
redis_handle
*
h
)
{
if
(
h
->
fd
>=
0
)
{
close
(
h
->
fd
);
h
->
fd
=
-
1
;
}
return
REDIS_OK
;
}
int
redis_handle_destroy
(
redis_handle
*
h
)
{
redis_handle_close
(
h
);
redis_parser_destroy
(
&
h
->
parser
);
return
REDIS_OK
;
}
static
int
redis__nonblock
(
int
fd
,
int
nonblock
)
{
int
flags
;
if
((
flags
=
fcntl
(
fd
,
F_GETFL
))
==
-
1
)
{
return
-
1
;
}
if
(
nonblock
)
{
flags
|=
O_NONBLOCK
;
}
else
{
flags
&=
~
O_NONBLOCK
;
}
if
(
fcntl
(
fd
,
F_SETFL
,
flags
)
==
-
1
)
{
return
-
1
;
}
return
0
;
}
static
int
redis__so_error
(
int
fd
)
{
int
err
=
0
;
socklen_t
errlen
=
sizeof
(
err
);
if
(
getsockopt
(
fd
,
SOL_SOCKET
,
SO_ERROR
,
&
err
,
&
errlen
)
==
-
1
)
{
return
-
1
;
}
return
err
;
}
static
int
redis__handle_connect
(
int
family
,
struct
sockaddr
*
addr
,
socklen_t
addrlen
)
{
int
fd
;
int
on
=
1
;
if
((
fd
=
socket
(
family
,
SOCK_STREAM
,
0
))
==
-
1
)
{
return
-
1
;
}
if
(
family
==
AF_INET
||
family
==
AF_INET6
)
{
if
(
setsockopt
(
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
on
,
sizeof
(
on
))
==
-
1
)
{
close
(
fd
);
return
-
1
;
}
}
/* The socket needs to be non blocking to be able to timeout connect(2). */
if
(
redis__nonblock
(
fd
,
1
)
==
-
1
)
{
close
(
fd
);
return
-
1
;
}
if
(
connect
(
fd
,
addr
,
addrlen
)
==
-
1
)
{
if
(
errno
==
EINPROGRESS
)
{
/* The user should figure out if connect(2) succeeded */
}
else
{
close
(
fd
);
return
-
1
;
}
}
if
(
setsockopt
(
fd
,
IPPROTO_TCP
,
TCP_NODELAY
,
&
on
,
sizeof
(
on
))
==
-
1
)
{
close
(
fd
);
return
-
1
;
}
return
fd
;
}
int
redis_handle_connect_in
(
redis_handle
*
h
,
struct
sockaddr_in
addr
)
{
int
fd
;
if
(
h
->
fd
>=
0
)
{
errno
=
EALREADY
;
return
REDIS_ESYS
;
}
assert
(
addr
.
sin_family
==
AF_INET
);
fd
=
redis__handle_connect
(
AF_INET
,
(
struct
sockaddr
*
)
&
addr
,
sizeof
(
addr
));
if
(
fd
==
-
1
)
{
return
REDIS_ESYS
;
}
h
->
fd
=
fd
;
return
REDIS_OK
;
}
int
redis_handle_connect_in6
(
redis_handle
*
h
,
struct
sockaddr_in6
addr
)
{
int
fd
;
if
(
h
->
fd
>=
0
)
{
errno
=
EALREADY
;
return
REDIS_ESYS
;
}
assert
(
addr
.
sin6_family
==
AF_INET6
);
fd
=
redis__handle_connect
(
AF_INET6
,
(
struct
sockaddr
*
)
&
addr
,
sizeof
(
addr
));
if
(
fd
==
-
1
)
{
return
REDIS_ESYS
;
}
h
->
fd
=
fd
;
return
REDIS_OK
;
}
int
redis_handle_connect_un
(
redis_handle
*
h
,
struct
sockaddr_un
addr
)
{
int
fd
;
if
(
h
->
fd
>=
0
)
{
errno
=
EALREADY
;
return
REDIS_ESYS
;
}
assert
(
addr
.
sun_family
==
AF_LOCAL
);
fd
=
redis__handle_connect
(
AF_LOCAL
,
(
struct
sockaddr
*
)
&
addr
,
sizeof
(
addr
));
if
(
fd
==
-
1
)
{
return
REDIS_ESYS
;
}
h
->
fd
=
fd
;
return
REDIS_OK
;
}
int
redis_handle_connect_gai
(
redis_handle
*
h
,
int
family
,
const
char
*
addr
,
int
port
)
{
char
_port
[
6
];
/* strlen("65535"); */
struct
addrinfo
hints
,
*
servinfo
,
*
p
;
int
rv
,
fd
;
snprintf
(
_port
,
6
,
"%d"
,
port
);
memset
(
&
hints
,
0
,
sizeof
(
hints
));
hints
.
ai_family
=
family
;
hints
.
ai_socktype
=
SOCK_STREAM
;
if
((
rv
=
getaddrinfo
(
addr
,
_port
,
&
hints
,
&
servinfo
))
!=
0
)
{
errno
=
rv
;
return
REDIS_EGAI
;
}
/* Expect at least one record. */
assert
(
servinfo
!=
NULL
);
for
(
p
=
servinfo
;
p
!=
NULL
;
p
=
p
->
ai_next
)
{
fd
=
redis__handle_connect
(
p
->
ai_family
,
p
->
ai_addr
,
p
->
ai_addrlen
);
if
(
fd
==
-
1
)
{
if
(
errno
==
EHOSTUNREACH
)
{
/* AF_INET6 record on a machine without IPv6 support.
* See c4ed06d9 for more information. */
continue
;
}
goto
error
;
}
freeaddrinfo
(
servinfo
);
h
->
fd
=
fd
;
return
REDIS_OK
;
}
error:
freeaddrinfo
(
servinfo
);
return
REDIS_ESYS
;
}
static
int
redis__select
(
int
mode
,
int
fd
,
struct
timeval
timeout
)
{
fd_set
rfd
,
wfd
;
fd_set
*
_set
=
NULL
,
*
_rfd
=
NULL
,
*
_wfd
=
NULL
;
int
so_error
;
switch
(
mode
)
{
case
REDIS__READABLE
:
FD_ZERO
(
&
rfd
);
FD_SET
(
fd
,
&
rfd
);
_rfd
=
_set
=
&
rfd
;
break
;
case
REDIS__WRITABLE
:
FD_ZERO
(
&
wfd
);
FD_SET
(
fd
,
&
wfd
);
_wfd
=
_set
=
&
wfd
;
break
;
default:
assert
(
NULL
&&
"invalid mode"
);
}
if
(
select
(
FD_SETSIZE
,
_rfd
,
_wfd
,
NULL
,
&
timeout
)
==
-
1
)
{
return
-
1
;
}
/* Not in set means select(2) timed out */
if
(
!
FD_ISSET
(
fd
,
_set
))
{
errno
=
ETIMEDOUT
;
return
-
1
;
}
/* Check for socket errors. */
so_error
=
redis__so_error
(
fd
);
if
(
so_error
==
-
1
)
{
return
-
1
;
}
if
(
so_error
)
{
/* Act as if the socket error occured with select(2). */
errno
=
so_error
;
return
-
1
;
}
return
0
;
}
static
int
redis__wait
(
redis_handle
*
h
,
int
mode
)
{
int
rv
;
if
(
h
->
fd
<
0
)
{
errno
=
EINVAL
;
return
REDIS_ESYS
;
}
rv
=
redis__select
(
mode
,
h
->
fd
,
h
->
timeout
);
if
(
rv
<
0
)
{
return
REDIS_ESYS
;
}
return
REDIS_OK
;
}
int
redis_handle_wait_connected
(
redis_handle
*
h
)
{
return
redis__wait
(
h
,
REDIS__WRITABLE
);
}
int
redis_handle_wait_readable
(
redis_handle
*
h
)
{
return
redis__wait
(
h
,
REDIS__READABLE
);
}
int
redis_handle_wait_writable
(
redis_handle
*
h
)
{
return
redis__wait
(
h
,
REDIS__WRITABLE
);
}
handle.h
0 → 100644
View file @
aacf12c2
#ifndef _HIREDIS_HANDLE_H
#define _HIREDIS_HANDLE_H 1
/* struct sockaddr_(in|in6|un) */
#include <netinet/in.h>
#include <sys/un.h>
/* struct timeval */
#include <sys/time.h>
/* local */
#include "parser.h"
#define REDIS_OK 0
#define REDIS_ESYS -1
#define REDIS_EGAI -2
#define REDIS_EPARSER -3
typedef
struct
redis_handle_s
redis_handle
;
struct
redis_handle_s
{
int
fd
;
struct
timeval
timeout
;
redis_parser
parser
;
};
int
redis_handle_init
(
redis_handle
*
h
);
int
redis_handle_close
(
redis_handle
*
);
int
redis_handle_destroy
(
redis_handle
*
);
int
redis_handle_set_timeout
(
redis_handle
*
,
unsigned
long
us
);
int
redis_handle_connect_in
(
redis_handle
*
,
struct
sockaddr_in
addr
);
int
redis_handle_connect_in6
(
redis_handle
*
,
struct
sockaddr_in6
addr
);
int
redis_handle_connect_un
(
redis_handle
*
,
struct
sockaddr_un
addr
);
int
redis_handle_connect_gai
(
redis_handle
*
h
,
int
family
,
const
char
*
addr
,
int
port
);
int
redis_handle_wait_connected
(
redis_handle
*
);
int
redis_handle_wait_readable
(
redis_handle
*
);
int
redis_handle_wait_writable
(
redis_handle
*
);
#endif
test/test-handle.c
0 → 100644
View file @
aacf12c2
#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 "../handle.h"
#include "test-helper.h"
int
test_connect_in_refused
(
void
)
{
redis_handle
h
;
int
rv
;
rv
=
redis_handle_init
(
&
h
);
assert
(
rv
==
REDIS_OK
);
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_handle_connect_in
(
&
h
,
sa
);
assert_equal_int
(
rv
,
REDIS_OK
);
rv
=
redis_handle_wait_connected
(
&
h
);
assert_equal_int
(
rv
,
REDIS_ESYS
);
assert_equal_int
(
errno
,
ECONNREFUSED
);
redis_handle_destroy
(
&
h
);
return
0
;
}
int
test_connect_in6_refused
(
void
)
{
redis_handle
h
;
int
rv
;
rv
=
redis_handle_init
(
&
h
);
assert
(
rv
==
REDIS_OK
);
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_handle_connect_in6
(
&
h
,
sa
);
assert_equal_int
(
rv
,
REDIS_OK
);
rv
=
redis_handle_wait_connected
(
&
h
);
assert_equal_int
(
rv
,
REDIS_ESYS
);
assert_equal_int
(
errno
,
ECONNREFUSED
);
redis_handle_destroy
(
&
h
);
return
0
;
}
int
test_connect_un_noent
(
void
)
{
redis_handle
h
;
int
rv
;
rv
=
redis_handle_init
(
&
h
);
assert
(
rv
==
REDIS_OK
);
struct
sockaddr_un
sa
;
sa
.
sun_family
=
AF_LOCAL
;
strcpy
((
char
*
)
&
sa
.
sun_path
,
"/tmp/idontexist.sock"
);
rv
=
redis_handle_connect_un
(
&
h
,
sa
);
assert_equal_int
(
rv
,
REDIS_ESYS
);
assert_equal_int
(
errno
,
ENOENT
);
redis_handle_destroy
(
&
h
);
return
0
;
}
int
test_connect_timeout
(
void
)
{
redis_handle
h
;
long
long
t1
,
t2
;
int
rv
;
rv
=
redis_handle_init
(
&
h
);
assert
(
rv
==
REDIS_OK
);
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
);
rv
=
redis_handle_set_timeout
(
&
h
,
10000
);
assert
(
rv
==
REDIS_OK
);
rv
=
redis_handle_connect_in
(
&
h
,
sa
);
assert_equal_int
(
rv
,
REDIS_OK
);
t1
=
usec
();
rv
=
redis_handle_wait_connected
(
&
h
);
t2
=
usec
();
assert_equal_int
(
rv
,
REDIS_ESYS
);
assert_equal_int
(
errno
,
ETIMEDOUT
);
assert
((
t2
-
t1
)
<
15000
);
/* 5ms of slack should be enough */
redis_handle_destroy
(
&
h
);
return
0
;
}
int
test_connect_gai_unknown_host
(
void
)
{
redis_handle
h
;
int
rv
;
rv
=
redis_handle_init
(
&
h
);
assert
(
rv
==
REDIS_OK
);
rv
=
redis_handle_connect_gai
(
&
h
,
AF_INET
,
"idontexist.foo"
,
redis_port
());
assert_equal_int
(
rv
,
REDIS_EGAI
);
/* Don't care about the specific error for now. */
redis_handle_destroy
(
&
h
);
return
0
;
}
int
main
(
void
)
{
test_connect_in_refused
();
test_connect_in6_refused
();
test_connect_un_noent
();
test_connect_timeout
();
test_connect_gai_unknown_host
();
}
test/test-helper.h
View file @
aacf12c2
...
...
@@ -3,8 +3,14 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <assert.h>
/******************************************************************************/
/* ASSERTS ********************************************************************/
/******************************************************************************/
#define assert_equal(a, b, type, fmt) do { \
type a_ = (a); \
type b_ = (b); \
...
...
@@ -42,4 +48,36 @@
} \
} while(0)
/******************************************************************************/
/* TIMING *********************************************************************/
/******************************************************************************/
static
long
long
usec
(
void
)
{
struct
timeval
tv
;
gettimeofday
(
&
tv
,
NULL
);
return
(
long
long
)
tv
.
tv_sec
*
1000000
+
tv
.
tv_usec
;
}
/******************************************************************************/
/* MISC ***********************************************************************/
/******************************************************************************/
#define REDIS_DEFAULT_PORT 6379
static
int
redis_port
(
void
)
{
char
*
env
,
*
eptr
;
long
port
=
REDIS_DEFAULT_PORT
;
env
=
getenv
(
"REDIS_PORT"
);
if
(
env
!=
NULL
)
{
port
=
strtol
(
env
,
&
eptr
,
10
);
/* Reset to default when the var contains garbage. */
if
(
*
eptr
!=
'\0'
)
{
port
=
REDIS_DEFAULT_PORT
;
}
}
return
port
;
}
#endif
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment