Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/comstack-p.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
YAZ_BEGIN_CDECL

const char *yaz_tcpip_get_error_details(COMSTACK cs);
int yaz_tcpip_set_head_only(COMSTACK cs, int head_only);
#ifndef WIN32
int yaz_unix_set_head_only(COMSTACK cs, int head_only);
#endif

/*
* Parses an HTTP chunked body.
Expand Down
12 changes: 12 additions & 0 deletions src/comstack.c
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,18 @@ int cs_complete_auto_head(const char *buf, int len)
return cs_complete_auto_x(buf, len, 1);
}

int cs_set_head_only(COMSTACK cs, int head_only)
{
if (cs->type == tcpip_type || cs->type == ssl_type)
return yaz_tcpip_set_head_only(cs, head_only);
#ifndef WIN32
if (cs->type == unix_type)
return yaz_unix_set_head_only(cs, head_only);
#endif
cs->cerrno = CSOUTSTATE;
return -1;
}

void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
{
cs->max_recv_bytes = max_recv_bytes;
Expand Down
2 changes: 1 addition & 1 deletion src/tcpip.c
Original file line number Diff line number Diff line change
Expand Up @@ -1769,7 +1769,7 @@ int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
return 0;
}

int cs_set_head_only(COMSTACK cs, int head_only)
int yaz_tcpip_set_head_only(COMSTACK cs, int head_only)
{
int (*completer)(const char *buf, int len) =
head_only ? cs_complete_auto_head : cs_complete_auto;
Expand Down
13 changes: 13 additions & 0 deletions src/unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <yaz/errno.h>
#include <yaz/log.h>
#include <yaz/snprintf.h>
#include "comstack-p.h"

#ifndef YAZ_SOCKLEN_T
#define YAZ_SOCKLEN_T int
Expand Down Expand Up @@ -309,6 +310,18 @@ struct sockaddr_un *unix_strtoaddr(const char *str)
return &add;
}

int yaz_unix_set_head_only(COMSTACK cs, int head_only)
{
if (cs->type == unix_type)
{
unix_state *sp = (unix_state *)cs->cprivate;
sp->complete = head_only ? cs_complete_auto_head : cs_complete_auto;
return 0;
}
cs->cerrno = CSOUTSTATE;
return -1;
}

static int unix_more(COMSTACK h)
{
unix_state *sp = (unix_state *)h->cprivate;
Expand Down
6 changes: 6 additions & 0 deletions src/yaz/comstack.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ YAZ_EXPORT void cs_print_session_info(COMSTACK cs);
YAZ_EXPORT int cs_parse_host(const char *uri, const char **host,
CS_TYPE *t, enum oid_proto *proto,
char **connect_host);
/** \brief Selects HTTP header-only framing for TCP/IP, SSL or UNIX transports
\param cs COMSTACK handle
\param head_only nonzero to stop at the end of HTTP headers; zero to
restore normal message framing
\returns 0 on success, -1 with CSOUTSTATE for unsupported transports
*/
YAZ_EXPORT int cs_set_head_only(COMSTACK cs, int head_only);

/*
Expand Down
82 changes: 81 additions & 1 deletion test/test_comstack.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
#include <yaz/test.h>
#include <yaz/comstack.h>
#include <yaz/tcpip.h>
#include <yaz/unix.h>
#include <yaz/xmalloc.h>

#ifndef WIN32
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#endif

static void tst_http_request(void)
{
Expand Down Expand Up @@ -489,9 +497,9 @@ static void tst_cs_get_error_tcp(void)
cs_close(cs);
}

#if HAVE_GNUTLS_H
static void tst_cs_get_error_ssl(void)
{
#if HAVE_GNUTLS_H
const char *details = NULL;
COMSTACK ssl_cs =
cs_create(ssl_type, CS_FLAGS_BLOCKING, PROTO_Z3950);
Expand All @@ -507,6 +515,75 @@ static void tst_cs_get_error_ssl(void)
YAZ_CHECK_EQ(cs_get_error(ssl_cs, &details), CSERRORSSL);
YAZ_CHECK(details && *details);
cs_close(ssl_cs);
}
#endif

#ifndef WIN32
static void tst_head_only_transport(CS_TYPE type)
{
static const char head[] =
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n";
static const char response[] =
"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata";
int fd[2], r, saved_errno;
COMSTACK cs;
char *buf = 0;
int bufsize = 0;

r = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
saved_errno = errno;
if (r == -1 && (saved_errno == EPERM || saved_errno == EACCES))
{
fprintf(stderr, "Skipping head-only framing: socketpair denied\n");
return;
}
YAZ_CHECK_EQ(r, 0);
if (r != 0)
return;
cs = cs_createbysocket(fd[0], type, CS_FLAGS_BLOCKING, PROTO_HTTP);
YAZ_CHECK(cs);
if (!cs)
{
close(fd[0]);
close(fd[1]);
return;
}

/* A HEAD response has no body despite Content-Length. Follow it with
a normal response to verify switching back, including buffered data.
The SSL transport has no TLS session here; only framing is tested. */
YAZ_CHECK_EQ(write(fd[1], head, sizeof(head) - 1), sizeof(head) - 1);
YAZ_CHECK_EQ(write(fd[1], response, sizeof(response) - 1),
sizeof(response) - 1);
close(fd[1]);
YAZ_CHECK_EQ(cs_set_head_only(cs, 1), 0);
r = cs_get(cs, &buf, &bufsize);
YAZ_CHECK_EQ(r, sizeof(head) - 1);
if (r == sizeof(head) - 1)
YAZ_CHECK(!memcmp(buf, head, r));
YAZ_CHECK_EQ(cs_set_head_only(cs, 0), 0);
r = cs_get(cs, &buf, &bufsize);
YAZ_CHECK_EQ(r, sizeof(response) - 1);
if (r == sizeof(response) - 1)
YAZ_CHECK(!memcmp(buf, response, r));
cs_close(cs);
xfree(buf);
}
#endif

static void tst_cs_set_head_only(void)
{
struct comstack unsupported;

memset(&unsupported, 0, sizeof(unsupported));
YAZ_CHECK_EQ(cs_set_head_only(&unsupported, 1), -1);
YAZ_CHECK_EQ(cs_errno(&unsupported), CSOUTSTATE);
#ifndef WIN32
tst_head_only_transport(tcpip_type);
tst_head_only_transport(unix_type);
#if HAVE_GNUTLS_H
tst_head_only_transport(ssl_type);
#endif
#endif
}

Expand All @@ -520,7 +597,10 @@ int main (int argc, char **argv)
tst_http_response();
tst_cs_get_host_args();
tst_cs_get_error_tcp();
#if HAVE_GNUTLS_H
tst_cs_get_error_ssl();
#endif
tst_cs_set_head_only();
YAZ_CHECK_TERM;
}

Expand Down
Loading