Skip to content
Snippets Groups Projects
README.coding.md 52.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • Andy Green's avatar
    Andy Green committed
    
    at cmake configure-time.  The user application may use one of the
    context init options flags
    
    
    	LWS_SERVER_OPTION_LIBEV
    	LWS_SERVER_OPTION_LIBUV
    
    Andy Green's avatar
    Andy Green committed
    
    
    to indicate it will use one of the event libraries at runtime.
    
    libev has some problems, its headers conflict with libevent, they both define
    critical constants like EV_READ to different values.  Attempts
    to discuss clearing that up with libevent and libev did not get anywhere useful.
    
    In addition building anything with libev using gcc spews warnings, the
    maintainer is aware of this for many years, and blames gcc.  We worked
    around this by disabling -Werror on the parts of lws that use libev.
    
    For these reasons and the response I got trying to raise these issues with
    them, if you have a choice about event loop, I would gently encourage you
    to avoid libev.  Where lws uses an event loop itself, eg in lwsws, we use
    libuv.
    
    Andy Green's avatar
    Andy Green committed
    @section extopts Extension option control from user code
    
    
    User code may set per-connection extension options now, using a new api
    
    `lws_set_extension_option()`.
    
    
    This should be called from the ESTABLISHED callback like this
    
    ```
    	 lws_set_extension_option(wsi, "permessage-deflate",
    	                          "rx_buf_size", "12"); /* 1 << 12 */
    ```
    
    
    If the extension is not active (missing or not negotiated for the
    connection, or extensions are disabled on the library) the call is
    just returns -1.  Otherwise the connection's extension has its
    named option changed.
    
    The extension may decide to alter or disallow the change, in the
    example above permessage-deflate restricts the size of his rx
    output buffer also considering the protocol's rx_buf_size member.
    
    
    Andy Green's avatar
    Andy Green committed
    
    @section httpsclient Client connections as HTTP[S] rather than WS[S]
    
    Andy Green's avatar
    Andy Green committed
    
    You may open a generic http client connection using the same
    struct lws_client_connect_info used to create client ws[s]
    connections.
    
    To stay in http[s], set the optional info member "method" to
    point to the string "GET" instead of the default NULL.
    
    After the server headers are processed, when payload from the
    server is available the callback LWS_CALLBACK_RECEIVE_CLIENT_HTTP
    will be made.
    
    You can choose whether to process the data immediately, or
    queue a callback when an outgoing socket is writeable to provide
    flow control, and process the data in the writable callback.
    
    
    Andy Green's avatar
    Andy Green committed
    Either way you use the api `lws_http_client_read()` to access the
    
    Andy Green's avatar
    Andy Green committed
    data, eg
    
    
    Andy Green's avatar
    Andy Green committed
    	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
    		{
    			char buffer[1024 + LWS_PRE];
    			char *px = buffer + LWS_PRE;
    			int lenx = sizeof(buffer) - LWS_PRE;
    
    			lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP\n");
    
    			/*
    			 * Often you need to flow control this by something
    			 * else being writable.  In that case call the api
    			 * to get a callback when writable here, and do the
    			 * pending client read in the writeable callback of
    			 * the output.
    			 */
    			if (lws_http_client_read(wsi, &px, &lenx) < 0)
    				return -1;
    			while (lenx--)
    				putchar(*px++);
    		}
    		break;
    
    Andy Green's avatar
    Andy Green committed
    Notice that if you will use SSL client connections on a vhost, you must
    prepare the client SSL context for the vhost after creating the vhost, since
    this is not normally done if the vhost was set up to listen / serve.  Call
    the api lws_init_vhost_client_ssl() to also allow client SSL on the vhost.
    
    
    Andy Green's avatar
    Andy Green committed
    @section clipipe Pipelining Client Requests to same host
    
    Andy Green's avatar
    Andy Green committed
    If you are opening more client requests to the same host and port, you
    can give the flag LCCSCF_PIPELINE on `info.ssl_connection` to indicate
    you wish to pipeline them.
    
    Without the flag, the client connections will occur concurrently using a
    socket and tls wrapper if requested for each connection individually.
    That is fast, but resource-intensive.
    
    With the flag, lws will queue subsequent client connections on the first
    connection to the same host and port.  When it has confirmed from the
    first connection that pipelining / keep-alive is supported by the server,
    it lets the queued client pipeline connections send their headers ahead
    of time to create a pipeline of requests on the server side.
    
    In this way only one tcp connection and tls wrapper is required to transfer
    all the transactions sequentially.  It takes a little longer but it
    can make a significant difference to resources on both sides.
    
    If lws learns from the first response header that keepalive is not possible,
    then it marks itself with that information and detaches any queued clients
    to make their own individual connections as a fallback.
    
    Andy Green's avatar
    Andy Green committed
    Lws can also intelligently combine multiple ongoing client connections to
    the same host and port into a single http/2 connection with multiple
    streams if the server supports it.
    
    Unlike http/1 pipelining, with http/2 the client connections all occur
    simultaneously using h2 stream multiplexing inside the one tcp + tls
    connection.
    
    You can turn off the h2 client support either by not building lws with
    `-DLWS_WITH_HTTP2=1` or giving the `LCCSCF_NOT_H2` flag in the client
    connection info struct `ssl_connection` member.
    
    
    Andy Green's avatar
    Andy Green committed
    @section vhosts Using lws vhosts
    
    Andy Green's avatar
    Andy Green committed
    
    If you set LWS_SERVER_OPTION_EXPLICIT_VHOSTS options flag when you create
    your context, it won't create a default vhost using the info struct
    members for compatibility.  Instead you can call lws_create_vhost()
    afterwards to attach one or more vhosts manually.
    
    ```
    
    	LWS_VISIBLE struct lws_vhost *
    	lws_create_vhost(struct lws_context *context,
    
    			 struct lws_context_creation_info *info);
    
    Andy Green's avatar
    Andy Green committed
    ```
    
    lws_create_vhost() uses the same info struct as lws_create_context(),
    it ignores members related to context and uses the ones meaningful
    for vhost (marked with VH in libwebsockets.h).
    
    ```
    
    	struct lws_context_creation_info {
    		int port;					/* VH */
    		const char *iface;				/* VH */
    		const struct lws_protocols *protocols;		/* VH */
    		const struct lws_extension *extensions;		/* VH */
    	...
    
    Andy Green's avatar
    Andy Green committed
    ```
    
    When you attach the vhost, if the vhost's port already has a listen socket
    then both vhosts share it and use SNI (is SSL in use) or the Host: header
    from the client to select the right one.  Or if no other vhost already
    listening the a new listen socket is created.
    
    There are some new members but mainly it's stuff you used to set at
    context creation time.
    
    
    
    Andy Green's avatar
    Andy Green committed
    @section sni How lws matches hostname or SNI to a vhost
    
    
    LWS first strips any trailing :port number.
    
    Then it tries to find an exact name match for a vhost listening on the correct
    port, ie, if SNI or the Host: header provided abc.com:1234, it will match on a
    vhost named abc.com that is listening on port 1234.
    
    If there is no exact match, lws will consider wildcard matches, for example
    if cats.abc.com:1234 is provided by the client by SNI or Host: header, it will
    accept a vhost "abc.com" listening on port 1234.  If there was a better, exact,
    match, it will have been chosen in preference to this.
    
    Connections with SSL will still have the client go on to check the
    certificate allows wildcards and error out if not.
     
    
    
    
    Andy Green's avatar
    Andy Green committed
    @section mounts Using lws mounts on a vhost
    
    Andy Green's avatar
    Andy Green committed
    
    The last argument to lws_create_vhost() lets you associate a linked
    list of lws_http_mount structures with that vhost's URL 'namespace', in
    a similar way that unix lets you mount filesystems into areas of your /
    filesystem how you like and deal with the contents transparently.
    
    ```
    
    	struct lws_http_mount {
    		struct lws_http_mount *mount_next;
    		const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
    		const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
    		const char *def; /* default target, eg, "index.html" */
    	
    		struct lws_protocol_vhost_options *cgienv;
    	
    		int cgi_timeout;
    		int cache_max_age;
    	
    		unsigned int cache_reusable:1;
    		unsigned int cache_revalidate:1;
    		unsigned int cache_intermediaries:1;
    	
    		unsigned char origin_protocol;
    		unsigned char mountpoint_len;
    	};
    
    Andy Green's avatar
    Andy Green committed
    ```
    
    The last mount structure should have a NULL mount_next, otherwise it should
    point to the 'next' mount structure in your list.
    
    Both the mount structures and the strings must persist until the context is
    destroyed, since they are not copied but used in place.
    
    `.origin_protocol` should be one of
    
    ```
    
    	enum {
    		LWSMPRO_HTTP,
    		LWSMPRO_HTTPS,
    		LWSMPRO_FILE,
    		LWSMPRO_CGI,
    		LWSMPRO_REDIR_HTTP,
    		LWSMPRO_REDIR_HTTPS,
    		LWSMPRO_CALLBACK,
    	};
    
     - LWSMPRO_FILE is used for mapping url namespace to a filesystem directory and
    
    Andy Green's avatar
    Andy Green committed
    serve it automatically.
    
    
     - LWSMPRO_CGI associates the url namespace with the given CGI executable, which
    
    runs when the URL is accessed and the output provided to the client.
    
     - LWSMPRO_REDIR_HTTP and LWSMPRO_REDIR_HTTPS auto-redirect clients to the given
    
    origin URL.
    
     - LWSMPRO_CALLBACK causes the http connection to attach to the callback
    
    associated with the named protocol (which may be a plugin).
    
    
    
    Andy Green's avatar
    Andy Green committed
    @section mountcallback Operation of LWSMPRO_CALLBACK mounts
    
    
    The feature provided by CALLBACK type mounts is binding a part of the URL
    namespace to a named protocol callback handler.
    
    This allows protocol plugins to handle areas of the URL namespace.  For example
    in test-server-v2.0.c, the URL area "/formtest" is associated with the plugin
    providing "protocol-post-demo" like this
    
    ```
    
    	static const struct lws_http_mount mount_post = {
    		NULL,		/* linked-list pointer to next*/
    		"/formtest",		/* mountpoint in URL namespace on this vhost */
    		"protocol-post-demo",	/* handler */
    		NULL,	/* default filename if none given */
    		NULL,
    		0,
    		0,
    		0,
    		0,
    		0,
    		LWSMPRO_CALLBACK,	/* origin points to a callback */
    		9,			/* strlen("/formtest"), ie length of the mountpoint */
    	};
    
    ```
    
    Client access to /formtest[anything] will be passed to the callback registered
    with the named protocol, which in this case is provided by a protocol plugin.
    
    Access by all methods, eg, GET and POST are handled by the callback.
    
    protocol-post-demo deals with accepting and responding to the html form that
    is in the test server HTML.
    
    When a connection accesses a URL related to a CALLBACK type mount, the
    connection protocol is changed until the next access on the connection to a
    URL outside the same CALLBACK mount area.  User space on the connection is
    arranged to be the size of the new protocol user space allocation as given in
    the protocol struct.
    
    This allocation is only deleted / replaced when the connection accesses a
    URL region with a different protocol (or the default protocols[0] if no
    CALLBACK area matches it).
    
    This "binding connection to a protocol" lifecycle in managed by
    `LWS_CALLBACK_HTTP_BIND_PROTOCOL` and `LWS_CALLBACK_HTTP_DROP_PROTOCOL`.
    Because of HTTP/1.1 connection pipelining, one connection may perform
    many transactions, each of which may map to different URLs and need
    binding to different protocols.  So these messages are used to
    create the binding of the wsi to your protocol including any
    allocations, and to destroy the binding, at which point you should
    destroy any related allocations.
    
    
    @section BINDTODEV SO_BIND_TO_DEVICE
    
    The .bind_iface flag in the context / vhost creation struct lets you
    declare that you want all traffic for listen and transport on that
    vhost to be strictly bound to the network interface named in .iface.
    
    This Linux-only feature requires SO_BIND_TO_DEVICE, which in turn
    requires CAP_NET_RAW capability... root has this capability.
    
    However this feature needs to apply the binding also to accepted
    sockets during normal operation, which implies the server must run
    the whole time as root.
    
    You can avoid this by using the Linux capabilities feature to have
    the unprivileged user inherit just the CAP_NET_RAW capability.
    
    You can confirm this with the test server
    
    
    ```
     $ sudo /usr/local/bin/libwebsockets-test-server -u agreen -i eno1 -k
    ```
    
    The part that ensures the capability is inherited by the unprivileged
    user is
    
    ```
    #if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
                            info.caps[0] = CAP_NET_RAW;
                            info.count_caps = 1;
    #endif
    ```
    
    
    
    @section dim Dimming webpage when connection lost
    
    The lws test plugins' html provides useful feedback on the webpage about if it
    is still connected to the server, by greying out the page if not.  You can
    also add this to your own html easily
    
     - include lws-common.js from your HEAD section
     
    
    Andy Green's avatar
    Andy Green committed
       \<script src="/lws-common.js">\</script>
    
       
     - dim the page during initialization, in a script section on your page
     
       lws_gray_out(true,{'zindex':'499'});
       
     - in your ws onOpen(), remove the dimming
     
       lws_gray_out(false);
       
     - in your ws onClose(), reapply the dimming
     
       lws_gray_out(true,{'zindex':'499'});
    
    Andy Green's avatar
    Andy Green committed
    
    @section errstyle Styling http error pages
    
    In the code, http errors should be handled by `lws_return_http_status()`.
    
    There are basically two ways... the vhost can be told to redirect to an "error
    page" URL in response to specifically a 404... this is controlled by the
    context / vhost info struct (`struct lws_context_creation_info`) member
    `.error_document_404`... if non-null the client is redirected to this string.
    
    If it wasn't redirected, then the response code html is synthesized containing
    the user-selected text message and attempts to pull in `/error.css` for styling.
    
    If this file exists, it can be used to style the error page.  See 
    https://libwebsockets.org/git/badrepo for an example of what can be done (
    and https://libwebsockets.org/error.css for the corresponding css).