webclient improvements
Highlights: * TLS support (a hook to allow users to provide TLS implementation) * ability to add extra request headers * ability to use PUT method * ability to report http status * error handling improvements Proposed on the ML while ago: https://www.mail-archive.com/dev@nuttx.apache.org/msg03803.html The original API is kept for now. I plan to remove them after adapting the existing users. (examples in this repo)
This commit is contained in:
parent
07f2318fae
commit
a1db71fa43
@ -103,6 +103,140 @@
|
||||
typedef void (*wget_callback_t)(FAR char **buffer, int offset,
|
||||
int datend, FAR int *buflen, FAR void *arg);
|
||||
|
||||
/* webclient_sink_callback_t: callback to consume data
|
||||
*
|
||||
* Same as wget_callback_t, but allowed to fail.
|
||||
*
|
||||
* Input Parameters:
|
||||
* Same as wget_callback_t
|
||||
*
|
||||
* Return value:
|
||||
* 0 on success.
|
||||
* A negative errno on error.
|
||||
*/
|
||||
|
||||
typedef CODE int (*webclient_sink_callback_t)(FAR char **buffer, int offset,
|
||||
int datend, FAR int *buflen,
|
||||
FAR void *arg);
|
||||
|
||||
/* webclient_body_callback_t: a callback to provide request body
|
||||
*
|
||||
* This callback can be called multiple times to provide
|
||||
* webclient_context::bodylen bytes of the data.
|
||||
* It's the responsibility of this callback to maintain the current
|
||||
* "offset" of the data. (similarly to fread(3))
|
||||
*
|
||||
* An implementation of this callback should perform either of
|
||||
* the followings:
|
||||
*
|
||||
* - fill the buffer (specified by buffer and *sizep) with the data
|
||||
* - update *datap with a buffer filled with the data
|
||||
*
|
||||
* Either ways, it should update *sizep to the size of the data.
|
||||
*
|
||||
* A short result is allowed. In that case, this callback will be called
|
||||
* again to provide the remaining data.
|
||||
*
|
||||
* Input Parameters:
|
||||
* buffer - The buffer to fill.
|
||||
* sizep - The size of buffer/data in bytes.
|
||||
* datap - The data to return.
|
||||
* ctx - The value of webclient_context::body_callback_arg.
|
||||
*
|
||||
* Return value:
|
||||
* 0 on success.
|
||||
* A negative errno on error.
|
||||
*/
|
||||
|
||||
typedef CODE int (*webclient_body_callback_t)(
|
||||
FAR void *buffer,
|
||||
FAR size_t *sizep,
|
||||
FAR const void * FAR *datap,
|
||||
FAR void *ctx);
|
||||
|
||||
struct webclient_tls_connection;
|
||||
|
||||
struct webclient_tls_ops
|
||||
{
|
||||
CODE int (*connect)(FAR void *ctx,
|
||||
FAR const char *hostname, FAR const char *port,
|
||||
unsigned int timeout_second,
|
||||
FAR struct webclient_tls_connection **connp);
|
||||
CODE ssize_t (*send)(FAR void *ctx,
|
||||
FAR struct webclient_tls_connection *conn,
|
||||
FAR const void *buf, size_t len);
|
||||
CODE ssize_t (*recv)(FAR void *ctx,
|
||||
FAR struct webclient_tls_connection *conn,
|
||||
FAR void *buf, size_t len);
|
||||
CODE int (*close)(FAR void *ctx,
|
||||
FAR struct webclient_tls_connection *conn);
|
||||
};
|
||||
|
||||
struct webclient_context
|
||||
{
|
||||
/* request parameters
|
||||
*
|
||||
* method - HTTP method like "GET", "POST".
|
||||
* The default value is "GET".
|
||||
* url - A pointer to a string containing the full URL.
|
||||
* (e.g., http://www.nutt.org/index.html, or
|
||||
* http://192.168.23.1:80/index.html)
|
||||
* headers - An array of pointers to the extra headers.
|
||||
* nheaders - The number of elements in the "headers" array.
|
||||
* bodylen - The size of the request body.
|
||||
*/
|
||||
|
||||
FAR const char *method;
|
||||
FAR const char *url;
|
||||
FAR const char * FAR const *headers;
|
||||
unsigned int nheaders;
|
||||
size_t bodylen;
|
||||
|
||||
/* other parameters
|
||||
*
|
||||
* buffer - A user provided buffer to receive the file data
|
||||
* (also used for the outgoing GET request)
|
||||
* It should be large enough to hold the whole
|
||||
* request header. It should also be large enough
|
||||
* to hold the whole response header.
|
||||
* buflen - The size of the user provided buffer
|
||||
* sink_callback - As data is obtained from the host, this function
|
||||
* is to dispose of each block of file data as it is
|
||||
* received.
|
||||
* callback - a compat version of sink_callback.
|
||||
* sink_callback_arg - User argument passed to callback.
|
||||
* body_callback - A callback function to provide the request body.
|
||||
* body_callback_arg - User argument passed to body_callback.
|
||||
* tls_ops - A vector to implement TLS operations.
|
||||
* NULL means no https support.
|
||||
* tls_ctx - A user pointer to be passed to tls_ops as it is.
|
||||
*/
|
||||
|
||||
FAR char *buffer;
|
||||
int buflen;
|
||||
wget_callback_t callback;
|
||||
webclient_sink_callback_t sink_callback;
|
||||
FAR void *sink_callback_arg;
|
||||
webclient_body_callback_t body_callback;
|
||||
FAR void *body_callback_arg;
|
||||
FAR const struct webclient_tls_ops *tls_ops;
|
||||
FAR void *tls_ctx;
|
||||
|
||||
/* results
|
||||
*
|
||||
* http_status - HTTP status code
|
||||
* http_reason - A buffer to store HTTP reason phrase.
|
||||
* If NULL, the reason phrase will be discarded.
|
||||
* http_reason_len - The size of the http_reason buffer
|
||||
* A reason phrase longer than the buffer size
|
||||
* will be silently truncated.
|
||||
*/
|
||||
|
||||
unsigned int http_status;
|
||||
FAR char *http_reason;
|
||||
size_t http_reason_len;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
@ -153,6 +287,12 @@ int wget(FAR const char *url, FAR char *buffer, int buflen,
|
||||
int wget_post(FAR const char *url, FAR const char *posts, FAR char *buffer,
|
||||
int buflen, wget_callback_t callback, FAR void *arg);
|
||||
|
||||
void webclient_set_defaults(FAR struct webclient_context *ctx);
|
||||
int webclient_perform(FAR struct webclient_context *ctx);
|
||||
void webclient_set_static_body(FAR struct webclient_context *ctx,
|
||||
FAR const void *body,
|
||||
size_t bodylen);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -158,6 +158,13 @@ struct wget_s
|
||||
char filename[CONFIG_WEBCLIENT_MAXFILENAME];
|
||||
};
|
||||
|
||||
struct conn
|
||||
{
|
||||
bool tls;
|
||||
int sockfd;
|
||||
struct webclient_tls_connection *tls_conn;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
@ -169,8 +176,6 @@ static const char g_httpcontenttype[] = "content-type: ";
|
||||
#endif
|
||||
static const char g_httphost[] = "host: ";
|
||||
static const char g_httplocation[] = "location: ";
|
||||
static const char g_httpget[] = "GET ";
|
||||
static const char g_httppost[] = "POST ";
|
||||
|
||||
static const char g_httpuseragentfields[] =
|
||||
"Connection: close\r\n"
|
||||
@ -178,10 +183,6 @@ static const char g_httpuseragentfields[] =
|
||||
CONFIG_NSH_WGET_USERAGENT
|
||||
"\r\n\r\n";
|
||||
|
||||
static const char g_http200[] = "200 ";
|
||||
static const char g_http301[] = "301 ";
|
||||
static const char g_http302[] = "302 ";
|
||||
|
||||
static const char g_httpcrnl[] = "\r\n";
|
||||
|
||||
static const char g_httpform[] = "Content-Type: "
|
||||
@ -196,10 +197,137 @@ static const char g_httpcache[] = "Cache-Control: no-cache";
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: conn_send
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t conn_send(struct webclient_context *ctx, struct conn *conn,
|
||||
const void *buffer, size_t len)
|
||||
{
|
||||
if (conn->tls)
|
||||
{
|
||||
return ctx->tls_ops->send(ctx->tls_ctx, conn->tls_conn, buffer, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
ssize_t ret = send(conn->sockfd, buffer, len, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: conn_send_all
|
||||
****************************************************************************/
|
||||
|
||||
static int conn_send_all(struct webclient_context *ctx, struct conn *conn,
|
||||
const void *buffer, size_t len)
|
||||
{
|
||||
size_t left = len;
|
||||
ssize_t ret;
|
||||
|
||||
while (left > 0)
|
||||
{
|
||||
ret = conn_send(ctx, conn, buffer, left);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout;
|
||||
}
|
||||
|
||||
buffer += ret;
|
||||
left -= ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: conn_recv
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t conn_recv(struct webclient_context *ctx, struct conn *conn,
|
||||
void *buffer, size_t len)
|
||||
{
|
||||
if (conn->tls)
|
||||
{
|
||||
return ctx->tls_ops->recv(ctx->tls_ctx, conn->tls_conn, buffer, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
ssize_t ret = recv(conn->sockfd, buffer, len, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: conn_close
|
||||
****************************************************************************/
|
||||
|
||||
static void conn_close(struct webclient_context *ctx, struct conn *conn)
|
||||
{
|
||||
if (conn->tls)
|
||||
{
|
||||
ctx->tls_ops->close(ctx->tls_ctx, conn->tls_conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
close(conn->sockfd);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: webclient_static_body_func
|
||||
****************************************************************************/
|
||||
|
||||
static int webclient_static_body_func(FAR void *buffer,
|
||||
FAR size_t *sizep,
|
||||
FAR const void * FAR *datap,
|
||||
FAR void *ctx)
|
||||
{
|
||||
*datap = ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: append
|
||||
****************************************************************************/
|
||||
|
||||
static char *append(char *dest, char *ep, const char *src)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (dest == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = strlen(src);
|
||||
if (ep - dest < len + 1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(dest, src, len);
|
||||
dest[len] = '\0';
|
||||
return dest + len;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: wget_strcpy
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef WGET_USE_URLENCODE
|
||||
static char *wget_strcpy(char *dest, const char *src)
|
||||
{
|
||||
int len = strlen(src);
|
||||
@ -208,6 +336,7 @@ static char *wget_strcpy(char *dest, const char *src)
|
||||
dest[len] = '\0';
|
||||
return dest + len;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: wget_urlencode_strcpy
|
||||
@ -229,7 +358,8 @@ static char *wget_urlencode_strcpy(char *dest, const char *src)
|
||||
* Name: wget_parsestatus
|
||||
****************************************************************************/
|
||||
|
||||
static inline int wget_parsestatus(struct wget_s *ws)
|
||||
static inline int wget_parsestatus(struct webclient_context *ctx,
|
||||
struct wget_s *ws)
|
||||
{
|
||||
int offset;
|
||||
int ndx;
|
||||
@ -247,22 +377,50 @@ static inline int wget_parsestatus(struct wget_s *ws)
|
||||
if ((strncmp(ws->line, g_http10, strlen(g_http10)) == 0) ||
|
||||
(strncmp(ws->line, g_http11, strlen(g_http11)) == 0))
|
||||
{
|
||||
unsigned long http_status;
|
||||
char *ep;
|
||||
|
||||
dest = &(ws->line[9]);
|
||||
ws->httpstatus = HTTPSTATUS_NONE;
|
||||
|
||||
/* Check for 200 OK */
|
||||
errno = 0;
|
||||
http_status = strtoul(dest, &ep, 10);
|
||||
if (ep == dest)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strncmp(dest, g_http200, strlen(g_http200)) == 0)
|
||||
if (*ep != ' ')
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (errno != 0)
|
||||
{
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ctx->http_status = http_status;
|
||||
if (ctx->http_reason != NULL)
|
||||
{
|
||||
strncpy(ctx->http_reason,
|
||||
ep + 1,
|
||||
ctx->http_reason_len - 1);
|
||||
ctx->http_reason[ctx->http_reason_len - 1] = 0;
|
||||
}
|
||||
|
||||
/* Check for 2xx (Successful) */
|
||||
|
||||
if ((http_status / 100) == 2)
|
||||
{
|
||||
ws->httpstatus = HTTPSTATUS_OK;
|
||||
}
|
||||
|
||||
/* Check for 301 Moved permanently or 302 Found.
|
||||
/* Check for 3xx (Redirection)
|
||||
* Location: header line will contain the new location.
|
||||
*/
|
||||
|
||||
else if (strncmp(dest, g_http301, strlen(g_http301)) == 0 ||
|
||||
strncmp(dest, g_http302, strlen(g_http302)) == 0)
|
||||
else if ((http_status / 100) == 3)
|
||||
{
|
||||
ws->httpstatus = HTTPSTATUS_MOVED;
|
||||
}
|
||||
@ -315,7 +473,14 @@ static int parseurl(FAR const char *url, FAR struct wget_s *ws)
|
||||
|
||||
if (url_s.port == 0)
|
||||
{
|
||||
ws->port = 80;
|
||||
if (!strcmp(ws->scheme, "https"))
|
||||
{
|
||||
ws->port = 443;
|
||||
}
|
||||
else
|
||||
{
|
||||
ws->port = 80;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -472,32 +637,15 @@ static int wget_gethostip(FAR char *hostname, FAR struct in_addr *dest)
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: wget_base
|
||||
*
|
||||
* Description:
|
||||
* Obtain the requested file from an HTTP server using the GET method.
|
||||
*
|
||||
* Input Parameters
|
||||
* url - A pointer to a string containing either the full URL to
|
||||
* the file to get (e.g., http://www.nutt.org/index.html, or
|
||||
* http://192.168.23.1:80/index.html).
|
||||
* buffer - A user provided buffer to receive the file data (also
|
||||
* used for the outgoing GET request
|
||||
* buflen - The size of the user provided buffer
|
||||
* callback - As data is obtained from the host, this function is
|
||||
* to dispose of each block of file data as it is received.
|
||||
* arg - User argument passed to callback.
|
||||
* mode - Indicates GET or POST modes
|
||||
* Name: webclient_perform
|
||||
*
|
||||
* Returned Value:
|
||||
* 0: if the GET operation completed successfully;
|
||||
* -1: On a failure with errno set appropriately
|
||||
* 0: if the operation completed successfully;
|
||||
* Negative errno: On a failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
wget_callback_t callback, FAR void *arg,
|
||||
FAR const char *posts, uint8_t mode)
|
||||
int webclient_perform(FAR struct webclient_context *ctx)
|
||||
{
|
||||
struct sockaddr_in server;
|
||||
struct wget_s *ws;
|
||||
@ -505,7 +653,12 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
bool redirected;
|
||||
unsigned int nredirect = 0;
|
||||
char *dest;
|
||||
int sockfd;
|
||||
char *ep;
|
||||
struct conn conn;
|
||||
FAR const struct webclient_tls_ops *tls_ops = ctx->tls_ops;
|
||||
FAR const char *method = ctx->method;
|
||||
FAR void *tls_ctx = ctx->tls_ctx;
|
||||
unsigned int i;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
@ -514,23 +667,22 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
ws = calloc(1, sizeof(struct wget_s));
|
||||
if (!ws)
|
||||
{
|
||||
return ERROR;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ws->buffer = buffer;
|
||||
ws->buflen = buflen;
|
||||
ws->buffer = ctx->buffer;
|
||||
ws->buflen = ctx->buflen;
|
||||
|
||||
/* Parse the hostname (with optional port number) and filename
|
||||
* from the URL.
|
||||
*/
|
||||
|
||||
ret = parseurl(url, ws);
|
||||
ret = parseurl(ctx->url, ws);
|
||||
if (ret != 0)
|
||||
{
|
||||
nwarn("WARNING: Malformed URL: %s\n", url);
|
||||
nwarn("WARNING: Malformed URL: %s\n", ctx->url);
|
||||
free(ws);
|
||||
errno = -ret;
|
||||
return ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ninfo("hostname='%s' filename='%s'\n", ws->hostname, ws->filename);
|
||||
@ -539,11 +691,19 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
|
||||
do
|
||||
{
|
||||
if (strcmp(ws->scheme, "http"))
|
||||
if (!strcmp(ws->scheme, "https") && tls_ops != NULL)
|
||||
{
|
||||
conn.tls = true;
|
||||
}
|
||||
else if (!strcmp(ws->scheme, "http"))
|
||||
{
|
||||
conn.tls = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
nerr("ERROR: unsupported scheme: %s\n", ws->scheme);
|
||||
free(ws);
|
||||
return ERROR;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Re-initialize portions of the state structure that could have
|
||||
@ -556,119 +716,164 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
ws->datend = 0;
|
||||
ws->ndx = 0;
|
||||
|
||||
/* Create a socket */
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0)
|
||||
if (conn.tls)
|
||||
{
|
||||
/* socket failed. It will set the errno appropriately */
|
||||
char port_str[sizeof("65535")];
|
||||
|
||||
nerr("ERROR: socket failed: %d\n", errno);
|
||||
free(ws);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Set send and receive timeout values */
|
||||
|
||||
tv.tv_sec = CONFIG_WEBCLIENT_TIMEOUT;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (FAR const void *)&tv,
|
||||
sizeof(struct timeval));
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (FAR const void *)&tv,
|
||||
sizeof(struct timeval));
|
||||
|
||||
/* Get the server address from the host name */
|
||||
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(ws->port);
|
||||
ret = wget_gethostip(ws->hostname, &server.sin_addr);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Could not resolve host (or malformed IP address) */
|
||||
|
||||
nwarn("WARNING: Failed to resolve hostname\n");
|
||||
ret = -EHOSTUNREACH;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
|
||||
/* Connect to server. First we have to set some fields in the
|
||||
* 'server' address structure. The system will assign me an arbitrary
|
||||
* local port that is not in use.
|
||||
*/
|
||||
|
||||
ret = connect(sockfd, (struct sockaddr *)&server,
|
||||
sizeof(struct sockaddr_in));
|
||||
if (ret < 0)
|
||||
{
|
||||
nerr("ERROR: connect failed: %d\n", errno);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Send the GET request */
|
||||
|
||||
dest = ws->buffer;
|
||||
if (mode == WGET_MODE_POST)
|
||||
{
|
||||
dest = wget_strcpy(dest, g_httppost);
|
||||
snprintf(port_str, sizeof(port_str), "%u", ws->port);
|
||||
ret = tls_ops->connect(tls_ctx, ws->hostname, port_str,
|
||||
CONFIG_WEBCLIENT_TIMEOUT, &conn.tls_conn);
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = wget_strcpy(dest, g_httpget);
|
||||
/* Create a socket */
|
||||
|
||||
conn.sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (conn.sockfd < 0)
|
||||
{
|
||||
/* socket failed. It will set the errno appropriately */
|
||||
|
||||
nerr("ERROR: socket failed: %d\n", errno);
|
||||
free(ws);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Set send and receive timeout values */
|
||||
|
||||
tv.tv_sec = CONFIG_WEBCLIENT_TIMEOUT;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
setsockopt(conn.sockfd, SOL_SOCKET, SO_RCVTIMEO,
|
||||
(FAR const void *)&tv, sizeof(struct timeval));
|
||||
setsockopt(conn.sockfd, SOL_SOCKET, SO_SNDTIMEO,
|
||||
(FAR const void *)&tv, sizeof(struct timeval));
|
||||
|
||||
/* Get the server address from the host name */
|
||||
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(ws->port);
|
||||
ret = wget_gethostip(ws->hostname, &server.sin_addr);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Could not resolve host (or malformed IP address) */
|
||||
|
||||
nwarn("WARNING: Failed to resolve hostname\n");
|
||||
ret = -EHOSTUNREACH;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
|
||||
/* Connect to server. First we have to set some fields in the
|
||||
* 'server' address structure. The system will assign me an
|
||||
* arbitrary local port that is not in use.
|
||||
*/
|
||||
|
||||
ret = connect(conn.sockfd, (struct sockaddr *)&server,
|
||||
sizeof(struct sockaddr_in));
|
||||
if (ret == -1)
|
||||
{
|
||||
ret = -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
nerr("ERROR: connect failed: %d\n", errno);
|
||||
free(ws);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Send the request */
|
||||
|
||||
dest = ws->buffer;
|
||||
ep = ws->buffer + ws->buflen;
|
||||
dest = append(dest, ep, method);
|
||||
dest = append(dest, ep, " ");
|
||||
|
||||
#ifndef WGET_USE_URLENCODE
|
||||
dest = wget_strcpy(dest, ws->filename);
|
||||
dest = append(dest, ep, ws->filename);
|
||||
#else
|
||||
/* TODO: should we use wget_urlencode_strcpy? */
|
||||
|
||||
dest = wget_strcpy(dest, ws->filename);
|
||||
dest = append(dest, ep, ws->filename);
|
||||
#endif
|
||||
|
||||
*dest++ = ISO_SPACE;
|
||||
dest = wget_strcpy(dest, g_http10);
|
||||
dest = wget_strcpy(dest, g_httpcrnl);
|
||||
dest = wget_strcpy(dest, g_httphost);
|
||||
dest = wget_strcpy(dest, ws->hostname);
|
||||
dest = wget_strcpy(dest, g_httpcrnl);
|
||||
dest = append(dest, ep, " ");
|
||||
dest = append(dest, ep, g_http10);
|
||||
dest = append(dest, ep, g_httpcrnl);
|
||||
dest = append(dest, ep, g_httphost);
|
||||
dest = append(dest, ep, ws->hostname);
|
||||
dest = append(dest, ep, g_httpcrnl);
|
||||
|
||||
if (mode == WGET_MODE_POST)
|
||||
for (i = 0; i < ctx->nheaders; i++)
|
||||
{
|
||||
int post_len;
|
||||
char post_size[8];
|
||||
|
||||
dest = wget_strcpy(dest, g_httpform);
|
||||
dest = wget_strcpy(dest, g_httpcrnl);
|
||||
dest = wget_strcpy(dest, g_httpcontsize);
|
||||
|
||||
/* Post content size */
|
||||
|
||||
post_len = strlen((char *)posts);
|
||||
sprintf(post_size, "%d", post_len);
|
||||
dest = wget_strcpy(dest, post_size);
|
||||
dest = wget_strcpy(dest, g_httpcrnl);
|
||||
dest = append(dest, ep, ctx->headers[i]);
|
||||
dest = append(dest, ep, g_httpcrnl);
|
||||
}
|
||||
|
||||
dest = wget_strcpy(dest, g_httpuseragentfields);
|
||||
if (mode == WGET_MODE_POST)
|
||||
if (ctx->bodylen)
|
||||
{
|
||||
dest = wget_strcpy(dest, (char *)posts);
|
||||
char post_size[sizeof("18446744073709551615")];
|
||||
|
||||
dest = append(dest, ep, g_httpcontsize);
|
||||
sprintf(post_size, "%zu", ctx->bodylen);
|
||||
dest = append(dest, ep, post_size);
|
||||
dest = append(dest, ep, g_httpcrnl);
|
||||
}
|
||||
|
||||
len = dest - buffer;
|
||||
dest = append(dest, ep, g_httpuseragentfields);
|
||||
|
||||
do
|
||||
if (dest == NULL)
|
||||
{
|
||||
ret = send(sockfd, buffer + ((dest - buffer) - len), len, 0);
|
||||
if (ret < 0)
|
||||
ret = -E2BIG;
|
||||
goto errout_with_errno;
|
||||
}
|
||||
|
||||
len = dest - ws->buffer;
|
||||
|
||||
ret = conn_send_all(ctx, &conn, ws->buffer, len);
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
size_t sent = 0;
|
||||
|
||||
while (sent < ctx->bodylen)
|
||||
{
|
||||
nerr("ERROR: send failed: %d\n", errno);
|
||||
goto errout;
|
||||
}
|
||||
FAR const void *input_buffer;
|
||||
size_t input_buffer_size = ws->buflen;
|
||||
|
||||
len -= ret;
|
||||
size_t todo = ctx->bodylen - sent;
|
||||
if (input_buffer_size > todo)
|
||||
{
|
||||
input_buffer_size = todo;
|
||||
}
|
||||
|
||||
input_buffer = ws->buffer;
|
||||
ret = ctx->body_callback(ws->buffer,
|
||||
&input_buffer_size,
|
||||
&input_buffer,
|
||||
ctx->body_callback_arg);
|
||||
if (ret < 0)
|
||||
{
|
||||
nerr("ERROR: body_callback failed: %d\n", -ret);
|
||||
goto errout_with_errno;
|
||||
}
|
||||
|
||||
ret = conn_send_all(ctx, &conn, input_buffer,
|
||||
input_buffer_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
sent += input_buffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
nerr("ERROR: send failed: %d\n", -ret);
|
||||
goto errout_with_errno;
|
||||
}
|
||||
while (len > 0);
|
||||
|
||||
/* Now loop to get the file sent in response to the GET. This
|
||||
* loop continues until either we read the end of file (nbytes == 0)
|
||||
@ -679,16 +884,16 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
redirected = false;
|
||||
for (; ; )
|
||||
{
|
||||
ws->datend = recv(sockfd, ws->buffer, ws->buflen, 0);
|
||||
ws->datend = conn_recv(ctx, &conn, ws->buffer, ws->buflen);
|
||||
if (ws->datend < 0)
|
||||
{
|
||||
nerr("ERROR: recv failed: %d\n", errno);
|
||||
goto errout;
|
||||
ret = ws->datend;
|
||||
nerr("ERROR: recv failed: %d\n", -ret);
|
||||
goto errout_with_errno;
|
||||
}
|
||||
else if (ws->datend == 0)
|
||||
{
|
||||
ninfo("Connection lost\n");
|
||||
close(sockfd);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -697,7 +902,7 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
ws->offset = 0;
|
||||
if (ws->state == WEBCLIENT_STATE_STATUSLINE)
|
||||
{
|
||||
ret = wget_parsestatus(ws);
|
||||
ret = wget_parsestatus(ctx, ws);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_errno;
|
||||
@ -725,8 +930,21 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
* received file.
|
||||
*/
|
||||
|
||||
callback(&ws->buffer, ws->offset, ws->datend,
|
||||
&buflen, arg);
|
||||
if (ctx->sink_callback)
|
||||
{
|
||||
ret = ctx->sink_callback(&ws->buffer, ws->offset,
|
||||
ws->datend, &ws->buflen,
|
||||
ctx->sink_callback_arg);
|
||||
if (ret != 0)
|
||||
{
|
||||
goto errout_with_errno;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->callback(&ws->buffer, ws->offset, ws->datend,
|
||||
&ws->buflen, ctx->sink_callback_arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -738,23 +956,25 @@ static int wget_base(FAR const char *url, FAR char *buffer, int buflen,
|
||||
goto errout;
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn_close(ctx, &conn);
|
||||
}
|
||||
while (redirected);
|
||||
|
||||
free(ws);
|
||||
return OK;
|
||||
|
||||
errout_with_errno:
|
||||
errno = -ret;
|
||||
errout:
|
||||
close(sockfd);
|
||||
ret = -errno;
|
||||
errout_with_errno:
|
||||
conn_close(ctx, &conn);
|
||||
|
||||
free(ws);
|
||||
return ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -863,7 +1083,23 @@ int web_posts_strlen(FAR char **name, FAR char **value, int len)
|
||||
int wget(FAR const char *url, FAR char *buffer, int buflen,
|
||||
wget_callback_t callback, FAR void *arg)
|
||||
{
|
||||
return wget_base(url, buffer, buflen, callback, arg, NULL, WGET_MODE_GET);
|
||||
struct webclient_context ctx;
|
||||
int ret;
|
||||
webclient_set_defaults(&ctx);
|
||||
ctx.method = "GET";
|
||||
ctx.url = url;
|
||||
ctx.buffer = buffer;
|
||||
ctx.buflen = buflen;
|
||||
ctx.callback = callback;
|
||||
ctx.sink_callback_arg = arg;
|
||||
ret = webclient_perform(&ctx);
|
||||
if (ret < 0)
|
||||
{
|
||||
errno = -ret;
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -873,6 +1109,48 @@ int wget(FAR const char *url, FAR char *buffer, int buflen,
|
||||
int wget_post(FAR const char *url, FAR const char *posts, FAR char *buffer,
|
||||
int buflen, wget_callback_t callback, FAR void *arg)
|
||||
{
|
||||
return wget_base(url, buffer, buflen, callback, arg, posts,
|
||||
WGET_MODE_POST);
|
||||
static const char *headers = g_httpform;
|
||||
struct webclient_context ctx;
|
||||
int ret;
|
||||
webclient_set_defaults(&ctx);
|
||||
ctx.method = "POST";
|
||||
ctx.url = url;
|
||||
ctx.buffer = buffer;
|
||||
ctx.buflen = buflen;
|
||||
ctx.callback = callback;
|
||||
ctx.sink_callback_arg = arg;
|
||||
ctx.headers = &headers;
|
||||
ctx.nheaders = 1;
|
||||
webclient_set_static_body(&ctx, posts, strlen(posts));
|
||||
ret = webclient_perform(&ctx);
|
||||
if (ret < 0)
|
||||
{
|
||||
errno = -ret;
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: webclient_set_defaults
|
||||
****************************************************************************/
|
||||
|
||||
void webclient_set_defaults(FAR struct webclient_context *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->method = "GET";
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: webclient_set_static_body
|
||||
****************************************************************************/
|
||||
|
||||
void webclient_set_static_body(FAR struct webclient_context *ctx,
|
||||
FAR const void *body,
|
||||
size_t bodylen)
|
||||
{
|
||||
ctx->body_callback = webclient_static_body_func;
|
||||
ctx->body_callback_arg = (void *)body; /* discard const */
|
||||
ctx->bodylen = bodylen;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user