Commit 14e11655 authored by Marko Mikulicic's avatar Marko Mikulicic Committed by rojer

Add mg_connect_ws helper

    PUBLISHED_FROM=0ff1c9becea2f0842dcf020d2d2ad64a8c5d95ae
parent c82ccd6c
......@@ -4159,6 +4159,10 @@ int mg_normalize_uri_path(const struct mg_str *in, struct mg_str *out) {
/* Amalgamated: #include "common/sha1.h" */
/* Amalgamated: #include "common/md5.h" */
#ifndef MG_DISABLE_HTTP_WEBSOCKET
#define MG_WS_NO_HOST_HEADER_MAGIC ((char *) 0x1)
#endif
enum mg_http_proto_data_type { DATA_NONE, DATA_FILE, DATA_PUT };
struct mg_http_proto_data_file {
......@@ -5365,9 +5369,11 @@ void mg_set_protocol_http_websocket(struct mg_connection *nc) {
#ifndef MG_DISABLE_HTTP_WEBSOCKET
void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri,
const char *extra_headers) {
unsigned long random = (unsigned long) uri;
void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path,
const char *host, const char *protocol,
const char *extra_headers) {
/* pretty poor source of randomness, TODO fix */
unsigned long random = (unsigned long) path;
char key[sizeof(random) * 3];
mg_base64_encode((unsigned char *) &random, sizeof(random), key);
......@@ -5376,9 +5382,26 @@ void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri,
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: %s\r\n"
"%s\r\n",
uri, key, extra_headers == NULL ? "" : extra_headers);
"Sec-WebSocket-Key: %s\r\n",
path, key);
/* TODO(mkm): take default hostname from http proto data if host == NULL */
if (host != MG_WS_NO_HOST_HEADER_MAGIC) {
mg_printf(nc, "Host: %s\r\n", host);
}
if (protocol != NULL) {
mg_printf(nc, "Sec-WebSocket-Protocol: %s\r\n", protocol);
}
if (extra_headers != NULL) {
mg_printf(nc, "%s", extra_headers);
}
mg_printf(nc, "\r\n");
}
void mg_send_websocket_handshake(struct mg_connection *nc, const char *path,
const char *extra_headers) {
mg_send_websocket_handshake2(nc, path, MG_WS_NO_HOST_HEADER_MAGIC, NULL,
extra_headers);
}
#endif /* MG_DISABLE_HTTP_WEBSOCKET */
......@@ -7357,52 +7380,110 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
#endif /* MG_DISABLE_FILESYSTEM */
struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
mg_event_handler_t ev_handler,
const char *url,
const char *extra_headers,
const char *post_data) {
struct mg_connection *nc = NULL;
char *addr = NULL;
const char *path = NULL;
int use_ssl = 0, addr_len = 0, port_i = -1;
if (memcmp(url, "http://", 7) == 0) {
url += 7;
} else if (memcmp(url, "https://", 8) == 0) {
url += 8;
use_ssl = 1;
/* returns 0 on success, -1 on error */
static int mg_http_common_url_parse(const char *url, const char *schema,
const char *schema_tls, int *use_ssl,
char **addr, int *port_i,
const char **path) {
int addr_len = 0;
if (memcmp(url, schema, strlen(schema)) == 0) {
url += strlen(schema);
} else if (memcmp(url, schema_tls, strlen(schema_tls)) == 0) {
url += strlen(schema_tls);
*use_ssl = 1;
#ifndef MG_ENABLE_SSL
return NULL; /* SSL is not enabled, cannot do HTTPS URLs */
return -1; /* SSL is not enabled, cannot do HTTPS URLs */
#endif
}
while (*url != '\0') {
addr = (char *) MG_REALLOC(addr, addr_len + 5 /* space for port too. */);
if (addr == NULL) {
*addr = (char *) MG_REALLOC(*addr, addr_len + 5 /* space for port too. */);
if (*addr == NULL) {
DBG(("OOM"));
return NULL;
return -1;
}
if (*url == '/') {
url++;
break;
}
if (*url == ':') port_i = addr_len;
addr[addr_len++] = *url;
addr[addr_len] = '\0';
if (*url == ':') *port_i = addr_len;
(*addr)[addr_len++] = *url;
(*addr)[addr_len] = '\0';
url++;
}
if (addr_len == 0) goto cleanup;
if (port_i < 0) {
port_i = addr_len;
strcpy(addr + port_i, use_ssl ? ":443" : ":80");
if (*port_i < 0) {
*port_i = addr_len;
strcpy(*addr + *port_i, *use_ssl ? ":443" : ":80");
} else {
port_i = -1;
*port_i = -1;
}
if (*path == NULL) *path = url;
if (**path == '\0') *path = "/";
DBG(("%s %s", *addr, *path));
return 0;
cleanup:
MG_FREE(*addr);
return -1;
}
struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
mg_event_handler_t ev_handler,
const char *url, const char *protocol,
const char *extra_headers) {
struct mg_connection *nc = NULL;
char *addr = NULL;
int port_i = -1;
const char *path = NULL;
int use_ssl = 0;
if (mg_http_common_url_parse(url, "ws://", "wss://", &use_ssl, &addr, &port_i,
&path) < 0) {
DBG(("%p: error parsing wss url: %s", (void *) nc, url));
return NULL;
}
if (path == NULL) path = url;
if ((nc = mg_connect(mgr, addr, ev_handler)) != NULL) {
mg_set_protocol_http_websocket(nc);
if (use_ssl) {
#ifdef MG_ENABLE_SSL
mg_set_ssl(nc, NULL, NULL);
#endif
}
/* If the port was addred by us, restore the original host. */
if (port_i >= 0) addr[port_i] = '\0';
mg_send_websocket_handshake2(nc, path, addr, protocol, extra_headers);
}
MG_FREE(addr);
return nc;
}
struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
mg_event_handler_t ev_handler,
const char *url,
const char *extra_headers,
const char *post_data) {
struct mg_connection *nc = NULL;
char *addr = NULL;
int port_i = -1;
const char *path = NULL;
int use_ssl = 0;
if (mg_http_common_url_parse(url, "http://", "https://", &use_ssl, &addr,
&port_i, &path) < 0) {
return NULL;
}
DBG(("%s %s", addr, path));
if ((nc = mg_connect(mgr, addr, ev_handler)) != NULL) {
mg_set_protocol_http_websocket(nc);
......@@ -7414,6 +7495,7 @@ struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
/* If the port was addred by us, restore the original host. */
if (port_i >= 0) addr[port_i] = '\0';
mg_printf(nc, "%s /%s HTTP/1.1\r\nHost: %s\r\nContent-Length: %" SIZE_T_FMT
"\r\n%s\r\n%s",
post_data == NULL ? "GET" : "POST", path, addr,
......@@ -7422,7 +7504,6 @@ struct mg_connection *mg_connect_http(struct mg_mgr *mgr,
post_data == NULL ? "" : post_data);
}
cleanup:
MG_FREE(addr);
return nc;
}
......
......@@ -1990,10 +1990,56 @@ void mg_set_protocol_http_websocket(struct mg_connection *nc);
* to fetch, extra_headers` is extra HTTP headers to send or `NULL`.
*
* This function is intended to be used by websocket client.
*
* Note that the Host header is mandatory in HTTP/1.1 and must be
* included in `extra_headers`. `mg_send_websocket_handshake2` offers
* a better API for that.
*
* Deprecated in favour of `mg_send_websocket_handshake2`
*/
void mg_send_websocket_handshake(struct mg_connection *nc, const char *uri,
const char *extra_headers);
/*
* Send websocket handshake to the server.
*
* `nc` must be a valid connection, connected to a server. `uri` is an URI
* to fetch, `host` goes into the `Host` header, `protocol` goes into the
* `Sec-WebSocket-Proto` header (NULL to omit), extra_headers` is extra HTTP
* headers to send or `NULL`.
*
* This function is intended to be used by websocket client.
*/
void mg_send_websocket_handshake2(struct mg_connection *nc, const char *path,
const char *host, const char *protocol,
const char *extra_headers);
/*
* Helper function that creates an outbound WebSocket connection.
*
* `url` is a URL to connect to. It must be properly URL-encoded, e.g. have
* no spaces, etc. By default, `mg_connect_ws()` sends Connection and
* Host headers. `extra_headers` is an extra HTTP headers to send, e.g.
* `"User-Agent: my-app\r\n"`.
* If `protocol` is not NULL, then a `Sec-WebSocket-Protocol` header is sent.
*
* Examples:
*
* [source,c]
* ----
* nc1 = mg_connect_ws(mgr, ev_handler_1, "ws://echo.websocket.org", NULL,
* NULL);
* nc2 = mg_connect_ws(mgr, ev_handler_1, "wss://echo.websocket.org", NULL,
* NULL);
* nc3 = mg_connect_ws(mgr, ev_handler_1, "ws://api.cesanta.com",
* "clubby.cesanta.com", NULL);
* ----
*/
struct mg_connection *mg_connect_ws(struct mg_mgr *mgr,
mg_event_handler_t event_handler,
const char *url, const char *protocol,
const char *extra_headers);
/*
* Send websocket frame to the remote end.
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment