1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/minimal-examples/http-client/minimal-http-client-post/minimal-http-client-post.c
2018-04-06 10:38:04 +08:00

244 lines
6.2 KiB
C

/*
* lws-minimal-http-client-post
*
* Copyright (C) 2018 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates the a minimal http client using lws and POST.
*
* It POSTs both form data and a file to the form at
* https://libwebsockets.org/testserver/formtest and dumps
* the html page received generated by the POST handler.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
static int interrupted;
static struct lws *client_wsi;
struct pss {
char boundary[32];
char body_part;
};
static int
callback_http(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct pss *pss = (struct pss *)user;
char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start,
*end = &buf[sizeof(buf) - 1];
uint8_t **up, *uend;
uint32_t r;
int n;
switch (reason) {
/* because we are protocols[0] ... */
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
in ? (char *)in : "(null)");
client_wsi = NULL;
break;
/* ...callbacks related to receiving the result... */
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
lwsl_user("LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: response %d\n",
lws_http_client_http_response(wsi));
break;
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
lwsl_hexdump_notice(in, len);
return 0; /* don't passthru */
case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
n = sizeof(buf) - LWS_PRE;
if (lws_http_client_read(wsi, &p, &n) < 0)
return -1;
return 0; /* don't passthru */
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
client_wsi = NULL;
break;
/* ...callbacks related to generating the POST... */
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
lwsl_user("LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER\n");
up = (uint8_t **)in;
uend = *up + len - 1;
/* generate a random boundary string */
lws_get_random(lws_get_context(wsi), &r, sizeof(r));
lws_snprintf(pss->boundary, sizeof(pss->boundary) - 1,
"---boundary-%08x", r);
n = lws_snprintf(buf, sizeof(buf) - 1,
"multipart/form-data; boundary=%s", pss->boundary);
if (lws_add_http_header_by_token(wsi,
WSI_TOKEN_HTTP_CONTENT_TYPE,
(uint8_t *)buf, n, up, uend))
return 1;
/*
* Notice because we are sending multipart/form-data we can
* usually rely on the server to understand where the form
* payload ends without having to give it an overall
* content-length (which can be troublesome to compute ahead
* of generating the data to send).
*
* Tell lws we are going to send the body next...
*/
lws_client_http_body_pending(wsi, 1);
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:
lwsl_user("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n");
n = LWS_WRITE_HTTP;
/*
* For a small body like this, we could prepare it in memory and
* send it all at once. But to show how to handle, eg,
* arbitrary-sized file payloads, or huge form-data fields, the
* sending is done in multiple passes through the event loop.
*/
switch (pss->body_part++) {
case 0:
/* notice every usage of the boundary starts with -- */
p += lws_snprintf(p, end - p, "--%s\xd\xa"
"content-disposition: "
"form-data; name=\"text\"\xd\xa"
"\xd\xa"
"my text field"
"\xd\xa", pss->boundary);
break;
case 1:
p += lws_snprintf(p, end - p,
"--%s\xd\xa"
"content-disposition: form-data; name=\"file\";"
"filename=\"myfile.txt\"\xd\xa"
"content-type: text/plain\xd\xa"
"\xd\xa"
"This is the contents of the "
"uploaded file.\xd\xa"
"\xd\xa", pss->boundary);
break;
case 2:
p += lws_snprintf(p, end - p, "--%s--\xd\xa",
pss->boundary);
lws_client_http_body_pending(wsi, 0);
/* necessary to support H2, it means we will write no
* more on this stream */
n = LWS_WRITE_HTTP_FINAL;
break;
default:
/*
* We can get extra callbacks here, if nothing to do,
* then do nothing.
*/
return 0;
}
if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), n)
!= lws_ptr_diff(p, start))
return 1;
if (n != LWS_WRITE_HTTP_FINAL)
lws_callback_on_writable(wsi);
return 0;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static const struct lws_protocols protocols[] = {
{
"http",
callback_http,
sizeof(struct pss),
0,
},
{ NULL, NULL, 0, 0 }
};
static void
sigint_handler(int sig)
{
interrupted = 1;
}
int main(int argc, char **argv)
{
struct lws_context_creation_info info;
struct lws_client_connect_info i;
struct lws_context *context;
int n = 0;
signal(SIGINT, sigint_handler);
lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
/* for LLL_ verbosity above NOTICE to be built into lws,
* lws must have been configured and built with
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
/* | LLL_DEBUG */, NULL);
lwsl_user("LWS minimal http client - POST\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
#if defined(LWS_WITH_MBEDTLS)
/*
* OpenSSL uses the system trust store. mbedTLS has to be told which
* CA to trust explicitly.
*/
info.client_ssl_ca_filepath = "./libwebsockets.org.cer";
#endif
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
i.port = 443;
i.address = "libwebsockets.org";
i.path = "/testserver/formtest";
i.ssl_connection = /* LCCSCF_NOT_H2 | */ LCCSCF_USE_SSL;
i.host = i.address;
i.origin = i.address;
i.method = "POST";
i.protocol = protocols[0].name;
i.pwsi = &client_wsi;
lws_client_connect_via_info(&i);
while (n >= 0 && client_wsi && !interrupted)
n = lws_service(context, 1000);
lws_context_destroy(context);
lwsl_user("Completed\n");
return 0;
}