Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serve static files after validating cookies on request in http_callback #3302

Open
BhanuPrakash-P opened this issue Dec 24, 2024 · 2 comments

Comments

@BhanuPrakash-P
Copy link

Hi Andy,

lws_http_mount.origin_protocol=LWSMPRO_FILE protocol is designed such a way that the static files get served automatically without hitting the http_callback.
If lws_http_mount.origin_protocol is set to LWSMPRO_CALLBACK, all the requests will hit the http_callback and files to be served using lws_serve_http_file(). For this lws_http_mount.origin is not considered, so path needs to be set properly before calling lws_serve_http_file().

Based on above understanding, I would like to get your inputs on the following.

  1. I would like to have cookie based authentication my server. So looking for any option in which static files gets served only after hitting the http_callback where I can validate the cookies and leave it to library to serve the static files. Is there any possibility to have two different protocols one for file and other for callback, still achieve the required functionality? Please share your opinions.

Sample Code of server:

static const struct lws_http_mount mount = {
	/* .mount_next */			NULL,	
	/* .mountpoint */			"/",		
	/* .origin */				"./testDir",
	/* .def */					"index.html",	
	/* .protocol */				"http-only",
	/* .cgienv */				NULL,
	/* .extra_mimetypes */		NULL,
	/* .interpret */			NULL,
	/* .cgi_timeout */			0,
	/* .cache_max_age */		0,
	/* .auth_mask */			0,
	/* .cache_reusable */		0,
	/* .cache_revalidate */		0,
	/* .cache_intermediaries */	0,
	/* .origin_protocol */		LWSMPRO_CALLBACK, 	
	/* .mountpoint_len */		1,		
	/* .basic_auth_login_file */NULL,
};

static struct lws_protocols protocols[] = {
        { "http-only", lws_callback_http, sizeof(struct per_session_data__post_demo_HTTP_CAllback), 4096, 0, NULL, 0},
      	LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO,
	LWS_PROTOCOL_LIST_TERM
};


int main(int argc, const char **argv)
{
	struct lws_context_creation_info info;
	memset(&info, 0, sizeof info);/* otherwise uninitialized garbage */
	info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS|
					LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS |
					LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT |
					LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT ;

	info.protocols = protocols;

	info.ssl_cert_filepath = "localhost-100y.cert";
	info.ssl_private_key_filepath = "localhost-100y.key";

	context = lws_create_context(&info);
	if(!context)
		printf("context creation failed");

	info.port = 80;
	info.mounts = &mount;
	info.vhost_name = "http";

	if(!lws_create_vhost(context, &info))
		printf("http vhost creation failed");

	info.port = 443;
	info.mounts = &mount;
	info.vhost_name = "https";
	if(!lws_create_vhost(context, &info))
		printf("https vhost creation failed");

	while (1)
	{
		n = lws_service(context,0);            
	}
	lws_context_destroy(context);		  
}
@BhanuPrakash-P
Copy link
Author

BhanuPrakash-P commented Dec 26, 2024

Hi @lws-team,

I had continued my analysis and learnt that LWS_CALLBACK_FILTER_HTTP_CONNECTION event received for all the requests including for static files. So, I set the mount.origin_protocol=LWSMPRO_FILE and handled cookie validation at LWS_CALLBACK_FILTER_HTTP_CONNECTION and proceed with the existing flow as mentioned below.
Incase of invalid cookie, returning -1 to end stream.

case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
      {
			const char *requested_uri = (const char*)in;
			printf("LWS_CALLBACK_FILTER_HTTP_CONNECTION: %s\n", requested_uri);
              
			//Check whether cookie validation needed for a requested URL
			bool needCookieValidation = isCookieValidationRequired(requested_uri);

			if(needCookieValidation)
			{
				//Check for cookies in the request headers
				char cookie[256] = {0};
				int n = lws_hdr_copy(wsi, cookie, sizeof(cookie), WSI_TOKEN_HTTP_COOKIE);
				if ((n > 0) && !strstr(cookie, "Cookie"))
				{
					printf("Received Cookie in the request header\r\n");
					if(!isThisValidCookie(cookie))
					{
						//Redirect to login.html
						if(lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
												(unsigned char *)"/login.html",
												11, &p, end)<0)
							return -1;
						return -1;
					}
				}
				else //Cookie is not part of request header
				{
						//Redirect to login.html
						if(lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,
												(unsigned char *)"/login.html",
												11, &p, end)<0)
							return -1;
						return -1;
				}
			}
			else
			{
				printf("Cookie validation not needed.. proceeding\r\n");
			}
      }
      break;

Is this a good approach or any better option is available? Please provide your suggestions.

Thanks,
Bhanu

@BhanuPrakash-P
Copy link
Author

BhanuPrakash-P commented Jan 24, 2025

Hi Andy,

I had used the above logic to handle cookie validation.
However, I came across a scenario where LWS_CALLBACK_FILTER_HTTP_CONNECTION is not triggered for the http request.

Scenario:
Send a HTTP request without a cookie which needs to be dropped. This request is triggering LWS_CALLBACK_FILTER_HTTP_CONNECTION, and -1 is returned. For the next HTTP request LWS_CALLBACK_FILTER_HTTP_CONNECTION is not called.

Working callback flow:
reason LWS_CALLBACK_FILTER_HTTP_CONNECTION:18 [wsisrv|3|adopted|h1|/|192.168.2.12|POST|200|/|192.168.2.12|P
No Cookie.. Redirecting to login
reason LWS_CALLBACK_HTTP_DROP_PROTOCOL:50 [wsisrv|3|adopted|h1|/|192.168.2.12|POST|200|/|192.168.2.12|P
reason LWS_CALLBACK_CLOSED_HTTP:5 [wsisrv|3|adopted|h1|/|192.168.2.12|POST|200|/|192.168.2.12|P
reason LWS_CALLBACK_WSI_DESTROY:30 [wsisrv|3|adopted|h1|/|192.168.2.12|POST|200|/|192.168.2.12|P

Non working callback flow:
reason LWS_CALLBACK_FILTER_NETWORK_CONNECTION:17 [wsi|2|listen|192.168.2.12||443]
reason LWS_CALLBACK_WSI_CREATE:29 [wsisrv|4|adopted]
reason LWS_CALLBACK_HTTP_BIND_PROTOCOL:49 [wsisrv|4|adopted|h1|/|192.168.2.12|POST]
reason LWS_CALLBACK_HTTP:12 [wsisrv|4|adopted|h1|/|192.168.2.12|POST]
reason LWS_CALLBACK_HTTP_BODY:13 [wsisrv|4|adopted|h1|/|192.168.2.12|POST]
reason LWS_CALLBACK_HTTP_BODY_COMPLETION:14 [wsisrv|4|adopted|h1|/|192.168.2.12|POST]
reason LWS_CALLBACK_HTTP_WRITEABLE:16 [wsisrv|4|adopted|h1|/|192.168.2.12|POST]
reason LWS_CALLBACK_HTTP_DROP_PROTOCOL:50 [wsisrv|4|adopted|h1|/|192.168.2.12|POST|200]
reason LWS_CALLBACK_HTTP_BIND_PROTOCOL:49 [wsisrv|4|adopted|h1|/|192.168.2.12|POST|200]

I also have couple of observations:

  1. my server has both websocket_callback and http_callback. some events are going to http_callback and some other events are going to websocket_callback.
  2. LWS_CALLBACK_FILTER_HTTP_CONNECTION is received on http_callback for every alternate HTTP request.

I tried returning 1 from LWS_CALLBACK_FILTER_HTTP_CONNECTION , still the behavior is same.
Any pointers to debug this? or is this expected behavior from the library?

Thanks,
Bhanu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant