848 lines
25 KiB
C
848 lines
25 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipike_http_fetcher.c,v $ $Revision: 1.4.10.1 $
|
|
* $Source: /home/interpeak/CVSRoot/ipike/src/ipike_http_fetcher.c,v $
|
|
* $Author: ulf $ $Date: 2011-11-16 10:33:33 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Erik Dahlberg <erik.dahlberg@windriver.com>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* Provides functionality to fetch files (certificates) remotely using HTTP */
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include "ipike_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
#include "ipike_h.h"
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_strlib.h>
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_sock2.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_tmo2.h>
|
|
|
|
#include "ipike_http_fetcher.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPIKE_HTTP_REQUEST_FMT_STRING "GET %s HTTP/1.1\r\nhost: %s\r\n\r\n"
|
|
#define IPIKE_HTTP_REQUEST_BUF_SIZE 256
|
|
#define IPIKE_HTTP_RESPONSE_BUF_SIZE_DEFAULT 4096
|
|
#define IPIKE_HTTP_RESPONSE_STATUS_OK "HTTP/1.1 200"
|
|
#define IPIKE_HTTP_RESPONSE_STATUS_OK_LEN 12
|
|
#define IPIKE_HTTP_PORT 80
|
|
#define IPIKE_HTTP_PREFIX "http://"
|
|
#define IPIKE_HTTP_PREFIX_LEN 7
|
|
|
|
/* HTTP socket state */
|
|
#define IPIKE_HTTP_SOCK_STATE_CREATED (1 << 0)
|
|
#define IPIKE_HTTP_SOCK_STATE_CONNECTED (1 << 1)
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* URL */
|
|
typedef struct Ipike_http_fetcher_url_st
|
|
{
|
|
char *address;
|
|
char *path;
|
|
} Ipike_http_fetcher_url;
|
|
|
|
|
|
/* HTTP fetcher context */
|
|
typedef struct Ipike_http_fetcher_ctx_st
|
|
{
|
|
Ipcom_list list; /* Context list */
|
|
Ipcom_tmo2 tmo; /* Timeout entry */
|
|
Ipike_http_fetcher_callback callback; /* User callback */
|
|
void *callback_ctx; /* User callback context */
|
|
Ip_fd fd; /* Socket filedescriptor */
|
|
Ipike_http_fetcher_url *url; /* URL */
|
|
Ip_u32 state; /* Socket state */
|
|
} Ipike_http_fetcher_ctx;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
IP_STATIC Ipcom_list ctx_list_head =
|
|
{
|
|
.next = &ctx_list_head,
|
|
.prev = &ctx_list_head,
|
|
.head = &ctx_list_head,
|
|
.size = 0
|
|
};
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipike_http_fetcher_url_alloc
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_http_fetcher_url *
|
|
ipike_http_fetcher_url_alloc()
|
|
{
|
|
Ipike_http_fetcher_url *url;
|
|
|
|
url = ipcom_malloc(sizeof(Ipike_http_fetcher_url));
|
|
|
|
if ( url )
|
|
ipcom_memset(url, 0, sizeof(Ipike_http_fetcher_url));
|
|
|
|
return url;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipike_http_fetcher_url_destroy
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_http_fetcher_url_destroy(Ipike_http_fetcher_url *url)
|
|
{
|
|
ip_assert(url);
|
|
|
|
if ( url->address )
|
|
ipcom_free(url->address);
|
|
if ( url->path )
|
|
ipcom_free(url->path);
|
|
|
|
ipcom_free(url);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipike_http_fetcher_ctx_alloc
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_http_fetcher_ctx *
|
|
ipike_http_fetcher_ctx_alloc()
|
|
{
|
|
Ipike_http_fetcher_ctx *ctx;
|
|
|
|
ctx = ipcom_malloc(sizeof(Ipike_http_fetcher_ctx));
|
|
|
|
if ( ctx )
|
|
{
|
|
ipcom_memset(ctx, 0, sizeof(Ipike_http_fetcher_ctx));
|
|
ctx->fd = IP_INVALID_SOCKET;
|
|
}
|
|
|
|
return ctx;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_ctx_destroy
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_http_fetcher_ctx_destroy(Ipike_http_fetcher_ctx *ctx)
|
|
{
|
|
ip_assert(ctx);
|
|
|
|
ipike_tmo2_cancel(&ctx->tmo);
|
|
|
|
if ( ctx->fd != IP_INVALID_SOCKET )
|
|
ipcom_socketclose(ctx->fd);
|
|
|
|
if ( ctx->url )
|
|
ipike_http_fetcher_url_destroy(ctx->url);
|
|
|
|
ipcom_free(ctx);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_tmo_cb
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipike_http_fetcher_tmo_cb(Ipike_http_fetcher_ctx *ctx)
|
|
{
|
|
|
|
if ( ctx->state == IPIKE_HTTP_SOCK_STATE_CREATED )
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipike_http_fetcher_tmo_cb() :: could not connect"\
|
|
" to %s",
|
|
ctx->url->address);
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipike_http_fetcher_tmo_cb() :: server %s did not"\
|
|
" respond",
|
|
ctx->url->address);
|
|
}
|
|
|
|
/* Notify failure to user callback */
|
|
ctx->callback(IPIKE_FAILED, IP_NULL, 0, ctx->callback_ctx);
|
|
|
|
ipcom_list_remove(&ctx->list);
|
|
|
|
ipike_http_fetcher_ctx_destroy(ctx);
|
|
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_url_parse_string
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC Ipike_http_fetcher_url *
|
|
ipike_http_fetcher_url_parse_string(const char *url_string)
|
|
{
|
|
Ipike_http_fetcher_url *url = IP_NULL;
|
|
const char *addr_ptr = IP_NULL;
|
|
const char *path_ptr = IP_NULL;
|
|
Ip_size_t addr_len = 0;
|
|
|
|
if ( ipcom_strncmp(url_string, IPIKE_HTTP_PREFIX, IPIKE_HTTP_PREFIX_LEN) != 0 )
|
|
goto error_out;
|
|
|
|
url = ipike_http_fetcher_url_alloc();
|
|
|
|
if ( !url )
|
|
goto error_out;
|
|
|
|
/* Make a local copy of the string without the url prefix */
|
|
addr_ptr = url_string + IPIKE_HTTP_PREFIX_LEN;
|
|
|
|
/* Get the start of the path */
|
|
path_ptr = ipcom_strchr(addr_ptr, '/');
|
|
|
|
/* Make sure we have ha '/' and something after it */
|
|
if ( !path_ptr || *path_ptr == '\0' )
|
|
goto error_out;
|
|
|
|
url->path = ipcom_strdup( path_ptr );
|
|
|
|
/* Get length of the address */
|
|
addr_len = (Ip_size_t) (path_ptr - addr_ptr);
|
|
|
|
url->address = ipcom_malloc( addr_len + 1 );
|
|
|
|
if ( !url->address )
|
|
goto error_out;
|
|
|
|
ipcom_strncpy( url->address, addr_ptr, addr_len );
|
|
url->address[addr_len] = '\0';
|
|
|
|
return url;
|
|
|
|
error_out:
|
|
|
|
if ( url )
|
|
ipike_http_fetcher_url_destroy( url );
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipike_http_fetcher_get_content
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC const char *
|
|
ipike_http_fetcher_get_content(
|
|
const char *http_response,
|
|
Ip_size_t http_response_len,
|
|
Ip_size_t *content_len)
|
|
{
|
|
const char *needle = "\r\n\r\n";
|
|
const char *ptr;
|
|
const char *end_ptr;
|
|
const char *needle_ptr;
|
|
|
|
end_ptr = http_response + http_response_len;
|
|
needle_ptr = needle;
|
|
|
|
/* Search for the "needle*" */
|
|
for (ptr = http_response; ptr < end_ptr; ptr++)
|
|
{
|
|
if ( *ptr == *needle_ptr )
|
|
needle_ptr++;
|
|
else
|
|
needle_ptr = needle;
|
|
|
|
if ( *needle_ptr == '\0' )
|
|
{
|
|
/* Match */
|
|
ptr++;
|
|
*content_len = (Ip_size_t) (end_ptr - ptr);
|
|
|
|
if ( *content_len == 0 )
|
|
return IP_NULL;
|
|
|
|
return ptr;
|
|
}
|
|
}
|
|
|
|
*content_len = 0;
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_process_response
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_http_fetcher_process_response( Ipike_http_fetcher_ctx *ctx )
|
|
{
|
|
char *resp_buf = IP_NULL;
|
|
const char *content = IP_NULL;
|
|
Ip_ssize_t recv_len = 0;
|
|
Ip_size_t content_len = 0;
|
|
Ip_size_t resp_buf_size = 0;
|
|
Ipike_error status = IPIKE_FAILED;;
|
|
|
|
ip_assert(ctx);
|
|
|
|
/* Remove the context from the list */
|
|
ipcom_list_remove(&ctx->list);
|
|
|
|
resp_buf_size = (Ip_size_t) ipcom_sysvar_get_as_int("ipike.http_response_buf_size",
|
|
IPIKE_HTTP_RESPONSE_BUF_SIZE_DEFAULT);
|
|
|
|
/* Allocate response buffer */
|
|
resp_buf = ipcom_malloc(resp_buf_size);
|
|
|
|
if ( !resp_buf )
|
|
{
|
|
IPCOM_LOG1(CRIT, "ipike_http_fetcher_process_response() :: "\
|
|
"failed to allocate response buffer (%lu)",
|
|
resp_buf_size);
|
|
goto done;
|
|
}
|
|
|
|
/* Receive data */
|
|
recv_len = ipcom_recv(ctx->fd, resp_buf, resp_buf_size, 0);
|
|
|
|
if ( recv_len < 0 )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher_process_response :: receive error \"%s\"",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto done;
|
|
}
|
|
|
|
/* Were we able to read all data */
|
|
if ( (Ip_size_t) recv_len == resp_buf_size )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher_process_response() :: "\
|
|
"response buffer to small (%lu)\n",
|
|
resp_buf_size);
|
|
goto done;
|
|
}
|
|
|
|
/* The received data must at least contain a HTTP OK code */
|
|
if ( recv_len <= IPIKE_HTTP_RESPONSE_STATUS_OK_LEN )
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipike_http_fetcher_process_response() :: "\
|
|
"not enough data in http response (%ld)",
|
|
recv_len);
|
|
goto done;
|
|
}
|
|
|
|
IPCOM_LOG1(DEBUG2, "ipike_http_fetcher_process_response() :: "\
|
|
"received %ld bytes of http data",
|
|
recv_len);
|
|
|
|
/* Check for HTTP OK */
|
|
if ( ipcom_strncmp( resp_buf, IPIKE_HTTP_RESPONSE_STATUS_OK,
|
|
IPIKE_HTTP_RESPONSE_STATUS_OK_LEN) != 0 )
|
|
{
|
|
IPCOM_LOG0(WARNING, "ipike_http_fetcher_process_response() :: "\
|
|
"no ok code in http response");
|
|
goto done;
|
|
}
|
|
|
|
/* Ignore the rest of the HTTP Header and just get the content */
|
|
content = ipike_http_fetcher_get_content( resp_buf,
|
|
(Ip_size_t) recv_len,
|
|
&content_len);
|
|
|
|
if ( !content )
|
|
{
|
|
IPCOM_LOG0(WARNING, "ipike_http_fetcher_process_response() :: "\
|
|
"no http content in response data");
|
|
goto done;
|
|
}
|
|
|
|
/* All success */
|
|
status = IPIKE_SUCCESS;;
|
|
|
|
done:
|
|
|
|
/* Call user callback */
|
|
ctx->callback(status, content, content_len, ctx->callback_ctx);
|
|
|
|
/* Free response buffer */
|
|
if ( resp_buf )
|
|
ipcom_free(resp_buf);
|
|
|
|
|
|
/* Context can now be destroyed */
|
|
ipike_http_fetcher_ctx_destroy(ctx);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_send_request
|
|
*===========================================================================
|
|
*/
|
|
IP_STATIC void
|
|
ipike_http_fetcher_send_request(Ipike_http_fetcher_ctx *ctx)
|
|
{
|
|
char http_request[IPIKE_HTTP_REQUEST_BUF_SIZE];
|
|
Ip_ssize_t len = 0;
|
|
Ip_socklen_t err_len = 0;
|
|
Ip_err err = 0;
|
|
|
|
ip_assert(ctx);
|
|
|
|
/* Remove the context from the list */
|
|
ipcom_list_remove(&ctx->list);
|
|
|
|
/* Cancel the connect timeout */
|
|
ipike_tmo2_cancel(&ctx->tmo);
|
|
|
|
/* In the normal case the socket comes from a created state, i.e.
|
|
* through an asynchronous (non-blocking) connect */
|
|
if ( ctx->state == IPIKE_HTTP_SOCK_STATE_CREATED )
|
|
{
|
|
/* Get any error on the socket */
|
|
err_len = sizeof(err);
|
|
|
|
if ( ipcom_getsockopt(ctx->fd, IP_SOL_SOCKET, IP_SO_ERROR, &err, &err_len) < 0 )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher_send_request() :: getsockopt failed (%s)",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Check if the connect operation was successful */
|
|
if ( err != 0 )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher_send_request() :: connect failed (%s)",
|
|
ipcom_strerror(err));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Connect operation was successful */
|
|
ctx->state = IPIKE_HTTP_SOCK_STATE_CONNECTED;
|
|
}
|
|
|
|
/* We should now have a connected socket */
|
|
ip_assert( ctx->state == IPIKE_HTTP_SOCK_STATE_CONNECTED );
|
|
|
|
/* Create HTTP Request */
|
|
len = ipcom_snprintf(http_request, sizeof(http_request),
|
|
IPIKE_HTTP_REQUEST_FMT_STRING,
|
|
ctx->url->path, ctx->url->address);
|
|
|
|
if ( len < 0 )
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_http_fetcher_send_request() :: "\
|
|
"failed to create http request");
|
|
goto error_out;
|
|
}
|
|
|
|
IPCOM_LOG1(DEBUG2, "ipike_http_fetcher_send_request() :: "\
|
|
"sending http request \n%s",
|
|
http_request);
|
|
|
|
/* Send HTTP Request */
|
|
if ( ipcom_send(ctx->fd, http_request, (Ip_size_t) len, 0) != len )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher_send_request() :: "\
|
|
"failed to send http request (%s)",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Add a timeout handler for the send operation */
|
|
if (ipcom_tmo2_request(ipike.tmo_head,
|
|
&ctx->tmo,
|
|
(Ipcom_tmo2_func) ipike_http_fetcher_tmo_cb,
|
|
ctx,
|
|
ipike.http_request_timeout * 1000u) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipike_http_fetcher_send_request() :: "\
|
|
"failed to register timeout");
|
|
goto error_out;
|
|
}
|
|
|
|
/* Insert into list */
|
|
ipcom_list_insert_last(&ctx_list_head, &ctx->list);
|
|
|
|
/* All success, lets wait for the response */
|
|
return;
|
|
|
|
error_out:
|
|
/* Notify failure to user */
|
|
ctx->callback(IPIKE_FAILED, IP_NULL, 0, ctx->callback_ctx);
|
|
|
|
/* Destroy context */
|
|
ipike_http_fetcher_ctx_destroy(ctx);
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_add_fds
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL int
|
|
ipike_http_fetcher_add_fds(Ip_fd_set *read_fds, Ip_fd_set *write_fds)
|
|
{
|
|
Ipike_http_fetcher_ctx *ctx;
|
|
int max = -1;
|
|
|
|
for ( ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head);
|
|
ctx != IP_NULL;
|
|
ctx = (Ipike_http_fetcher_ctx *) ipcom_list_next(&ctx->list) )
|
|
{
|
|
switch (ctx->state)
|
|
{
|
|
case IPIKE_HTTP_SOCK_STATE_CREATED:
|
|
/* Waiting for connect to finish */
|
|
IP_FD_SET(ctx->fd, write_fds);
|
|
break;
|
|
case IPIKE_HTTP_SOCK_STATE_CONNECTED:
|
|
/* Waiting for HTTP response */
|
|
IP_FD_SET(ctx->fd, read_fds);
|
|
break;
|
|
default:
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher_add_fds() :: "\
|
|
"invalid socket state (%u)",
|
|
ctx->state);
|
|
|
|
break;
|
|
}
|
|
max = IP_MAX(ctx->fd, max);
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_get
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL Ipike_error
|
|
ipike_http_fetcher_get(
|
|
const char *url_string,
|
|
Ipike_http_fetcher_callback callback,
|
|
void *callback_ctx)
|
|
{
|
|
union Ip_sockaddr_union server_addr;
|
|
struct Ip_addrinfo *result = IP_NULL;;
|
|
Ipike_http_fetcher_ctx *ctx = IP_NULL;
|
|
int i = 0;
|
|
|
|
IPCOM_LOG1(DEBUG2, "ipike_http_fetcher_get() :: fetching \"%s\"", url_string);
|
|
|
|
/* Allocate context for this request */
|
|
ctx = ipike_http_fetcher_ctx_alloc();
|
|
|
|
if ( !ctx )
|
|
{
|
|
IP_PANIC();
|
|
IPCOM_LOG0(CRIT, "ipike_http_fetcher_get() :: failed to allocate memory");
|
|
goto error_out;
|
|
}
|
|
|
|
/* Set user callback */
|
|
ctx->callback = callback;
|
|
ctx->callback_ctx = callback_ctx;
|
|
|
|
/* Parse URL */
|
|
ctx->url = ipike_http_fetcher_url_parse_string(url_string);
|
|
|
|
if ( !ctx->url )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher_get() :: invalid URL \"%s\"", url_string);
|
|
goto error_out;
|
|
}
|
|
|
|
memset(&server_addr, 0, sizeof(server_addr));
|
|
|
|
/* Do we have a DNS name */
|
|
if ( isalpha(*ctx->url->address) )
|
|
{
|
|
/* Resolve */
|
|
if ( ipcom_getaddrinfo(ctx->url->address, IP_NULL, IP_NULL, &result) != 0 )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: failed to resolve \"%s\"",
|
|
ctx->url->address);
|
|
goto error_out;
|
|
}
|
|
|
|
/* Use the first proposed address */
|
|
memcpy(&server_addr, result->ai_addr, result->ai_addrlen);
|
|
|
|
ipcom_freeaddrinfo(result);
|
|
}
|
|
else
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
if ( ipcom_inet_pton(IP_AF_INET, ctx->url->address, &server_addr.sin.sin_addr) == 1 )
|
|
{
|
|
server_addr.sa.sa_family = IP_AF_INET;
|
|
server_addr.sin.sin_port = ip_htons(IPIKE_HTTP_PORT);
|
|
IPCOM_SA_LEN_SET(&server_addr.sa, sizeof(struct Ip_sockaddr_in));
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if ( server_addr.sa.sa_family == 0 &&
|
|
ipcom_inet_pton(IP_AF_INET6, ctx->url->address, &server_addr.sin6.sin6_addr) == 1 )
|
|
{
|
|
server_addr.sa.sa_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(&server_addr.sa, sizeof(struct Ip_sockaddr_in6));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ( server_addr.sa.sa_family == 0 )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: bad address \"%s\"", ctx->url->address);
|
|
goto error_out;
|
|
}
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if ( server_addr.sa.sa_family == IP_AF_INET )
|
|
server_addr.sin.sin_port = ip_htons(IPIKE_HTTP_PORT);
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if ( server_addr.sa.sa_family == IP_AF_INET6 )
|
|
server_addr.sin6.sin6_port = ip_htons(IPIKE_HTTP_PORT);
|
|
#endif
|
|
|
|
/* Create socket */
|
|
if ( (ctx->fd = ipcom_socket(server_addr.sa.sa_family, IP_SOCK_STREAM, IP_IPPROTO_TCP)) < 0 )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: failed to create socket (%s)",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Set state to created */
|
|
ctx->state = IPIKE_HTTP_SOCK_STATE_CREATED;
|
|
|
|
i = 1;
|
|
/* Set socket to non-blocking */
|
|
if (ipcom_socketioctl(ctx->fd, IP_FIONBIO, (char *)&i) != 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipike_http_fetcher() :: failed to set socket to"\
|
|
" non blocking (%s)",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Connect */
|
|
if ( ipcom_connect(ctx->fd, (struct Ip_sockaddr *) &server_addr,
|
|
IPCOM_SA_LEN_GET(&server_addr.sa)) < 0 )
|
|
{
|
|
/* We expect IP_ERRNO_EINPROGRESS in the normal case, fail otherwise */
|
|
if ( ipcom_errno != IP_ERRNO_EINPROGRESS )
|
|
{
|
|
IPCOM_LOG1(ERR, "ipike_http_fetcher() :: failed to connect socket (%s)",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto error_out;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Socket successfully connected, can only occur if server address is a
|
|
* local address */
|
|
IPCOM_LOG0(DEBUG2, "ipike_http_fetcher() :: socket immediately connected");
|
|
ctx->state = IPIKE_HTTP_SOCK_STATE_CONNECTED;
|
|
}
|
|
|
|
i = 0;
|
|
/* Set socket to blocking */
|
|
if (ipcom_socketioctl(ctx->fd, IP_FIONBIO, (char *)&i) != 0)
|
|
{
|
|
IPCOM_LOG1(WARNING, "ipike_http_fetcher() :: failed to set socket to"\
|
|
" blocking (%s)",
|
|
ipcom_strerror(ipcom_errno));
|
|
goto error_out;
|
|
}
|
|
|
|
/* Add a timeout handler for connect operation */
|
|
if (ipcom_tmo2_request(ipike.tmo_head,
|
|
&ctx->tmo,
|
|
(Ipcom_tmo2_func) ipike_http_fetcher_tmo_cb,
|
|
ctx,
|
|
ipike.http_request_timeout * 1000u) != IPCOM_SUCCESS)
|
|
goto error_out;
|
|
|
|
/* Insert into list */
|
|
ipcom_list_insert_last(&ctx_list_head, &ctx->list);
|
|
|
|
/* It's unlikely but the socket can be in a connected state, in that case
|
|
* we can send the request immediately */
|
|
if ( ctx->state == IPIKE_HTTP_SOCK_STATE_CONNECTED )
|
|
ipike_http_fetcher_send_request(ctx);
|
|
|
|
/* return */
|
|
return IPIKE_SUCCESS;
|
|
|
|
error_out:
|
|
|
|
if ( ctx )
|
|
ipike_http_fetcher_ctx_destroy(ctx);
|
|
|
|
return IPIKE_FAILED;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_process
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL int
|
|
ipike_http_fetcher_process( Ip_fd_set *read_fds, Ip_fd_set *write_fds )
|
|
{
|
|
Ipike_http_fetcher_ctx *ctx = IP_NULL;
|
|
Ipike_http_fetcher_ctx *next = IP_NULL;
|
|
int num_processed = 0;
|
|
|
|
ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head);
|
|
|
|
while ( ctx )
|
|
{
|
|
next = (Ipike_http_fetcher_ctx *) ipcom_list_next(&ctx->list);
|
|
|
|
/* Socket ready for write? (connect done?)*/
|
|
if ( IP_FD_ISSET(ctx->fd, write_fds) )
|
|
{
|
|
/* Try to send the request */
|
|
ipike_http_fetcher_send_request(ctx);
|
|
|
|
num_processed++;
|
|
}
|
|
/* Socket ready for read? */
|
|
else if ( IP_FD_ISSET(ctx->fd, read_fds) )
|
|
{
|
|
/* Process response */
|
|
ipike_http_fetcher_process_response(ctx);
|
|
|
|
num_processed++;
|
|
}
|
|
|
|
ctx = next;
|
|
}
|
|
|
|
return num_processed;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipike_http_fetcher_cleanup
|
|
*===========================================================================
|
|
*/
|
|
IP_GLOBAL void
|
|
ipike_http_fetcher_cleanup(void)
|
|
{
|
|
Ipike_http_fetcher_ctx *ctx;
|
|
|
|
/* Close any pending http requests */
|
|
for ( ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head);
|
|
ctx != IP_NULL;
|
|
ctx = (Ipike_http_fetcher_ctx *) ipcom_list_first(&ctx_list_head) )
|
|
{
|
|
/* Notify failures to user callback */
|
|
ctx->callback(IPIKE_FAILED, IP_NULL, 0, ctx->callback_ctx);
|
|
|
|
/* Remove from list */
|
|
ipcom_list_remove(&ctx->list);
|
|
|
|
/* Destroy context */
|
|
ipike_http_fetcher_ctx_destroy(ctx);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|