| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 | /* * Copyright (c) 2018 the CivetWeb developers * Revisited version: Copyright (c) 2022 the CivetWeb developers * MIT License *//* Simple demo of a REST callback. */#ifdef _WIN32#include <windows.h>#else#include <unistd.h>#endif#include <stdarg.h>#include <stdlib.h>#include <string.h>#include <time.h>#include "cJSON.h"#include "civetweb.h"#define PORT "8089"#define HOST_INFO "http://localhost:8089"#define EXAMPLE_URI "/res/*/*"#define EXIT_URI "/exit"int exitNow = 0;static intSendJSON(struct mg_connection *conn, cJSON *json_obj){	char *json_str = cJSON_PrintUnformatted(json_obj);	size_t json_str_len = strlen(json_str);	/* Send HTTP message header (+1 for \n) */	mg_send_http_ok(conn, "application/json; charset=utf-8", json_str_len + 1);	/* Send HTTP message content */	mg_write(conn, json_str, json_str_len);	/* Add a newline. This is not required, but the result is more	 * human-readable in a debugger. */	mg_write(conn, "\n", 1);	/* Free string allocated by cJSON_Print* */	cJSON_free(json_str);	return (int)json_str_len;}static unsigned request = 0; /* demo data: request counter */static intExampleGET(struct mg_connection *conn, const char *p1, const char *p2){	cJSON *obj = cJSON_CreateObject();	if (!obj) {		/* insufficient memory? */		mg_send_http_error(conn, 500, "Server error");		return 500;	}	printf("GET %s/%s\n", p1, p2);	cJSON_AddStringToObject(obj, "version", CIVETWEB_VERSION);	cJSON_AddStringToObject(obj, "path1", p1);	cJSON_AddStringToObject(obj, "path2", p2);	cJSON_AddNumberToObject(obj, "request", ++request);	SendJSON(conn, obj);	cJSON_Delete(obj);	return 200;}static intExampleDELETE(struct mg_connection *conn, const char *p1, const char *p2){	printf("DELETE %s/%s\n", p1, p2);	mg_send_http_error(conn,	                   204,	                   "%s",	                   ""); /* Return "deleted" = "204 No Content" */	return 204;}static intExamplePUT(struct mg_connection *conn, const char *p1, const char *p2){	char buffer[1024];	int dlen = mg_read(conn, buffer, sizeof(buffer) - 1);	cJSON *obj, *elem;	unsigned newvalue;	printf("PUT %s/%s\n", p1, p2);	if ((dlen < 1) || (dlen >= sizeof(buffer))) {		mg_send_http_error(conn, 400, "%s", "No request body data");		return 400;	}	buffer[dlen] = 0;	obj = cJSON_Parse(buffer);	if (obj == NULL) {		mg_send_http_error(conn, 400, "%s", "Invalid request body data");		return 400;	}	elem = cJSON_GetObjectItemCaseSensitive(obj, "request");	if (!cJSON_IsNumber(elem)) {		cJSON_Delete(obj);		mg_send_http_error(conn,		                   400,		                   "%s",		                   "No \"request\" number in body data");		return 400;	}	newvalue = (unsigned)elem->valuedouble;	if ((double)newvalue != elem->valuedouble) {		cJSON_Delete(obj);		mg_send_http_error(conn,		                   400,		                   "%s",		                   "Invalid \"request\" number in body data");		return 400;	}	request = newvalue;	cJSON_Delete(obj);	mg_send_http_error(conn, 201, "%s", ""); /* Return "201 Created" */	return 201;}#if 0 /* Old version: User code had to split the url. */static intmg_vsplit(const char *url, const char *pattern, va_list va){	int ret = 0;	while (*url && *pattern) {		if (*url == *pattern) {			url++;			pattern++;		} else if (*pattern == '*') {			char *p = va_arg(va, char *);			size_t l = va_arg(va, size_t);			if (p == NULL || l == 0) {				return 0;			}			while ((*url != '/') && (*url != 0)) {				if (l == 0) {					return 0;				}				l--;				*p = *url;				p++;				url++;			}			*p = 0;			pattern++;			ret++;		} else {			return 0;		}	}	return ret;}static intmg_split(const char *url, const char *pattern, ...){	int ret;	va_list va;	va_start(va, pattern);	ret = mg_vsplit(url, pattern, va);	va_end(va);	return ret;}#endifstatic intExampleHandler(struct mg_connection *conn, void *cbdata){	char path1[1024], path2[1024];	const struct mg_request_info *ri = mg_get_request_info(conn);	const char *url = ri->local_uri;	size_t url_len = strlen(url);	/* Pattern matching */#if 0 /* Old version: User code had to split the url. */	if (2	    != mg_split(	           url, EXAMPLE_URI, path1, sizeof(path1), path2, sizeof(path2))) {		mg_send_http_error(conn, 404, "Invalid path: %s\n", url);		return 404;	}#else /* New version: User mg_match. */	struct mg_match_context mcx;	mcx.case_sensitive = 0;	ptrdiff_t ret = mg_match(EXAMPLE_URI, url, &mcx);	if ((ret != url_len) || (mcx.num_matches != 2)) {		/* Note: Could have done this with a $ at the end of the match		 * pattern as well. Then we would have to check for a return value		 * of -1 only. Here we use this version as minimum modification		 * of the existing code. */		printf("Match ret: %i\n", (int)ret);		mg_send_http_error(conn, 404, "Invalid path: %s\n", url);		return 404;	}	memcpy(path1, mcx.match[0].str, mcx.match[0].len);	path1[mcx.match[0].len] = 0;	memcpy(path2, mcx.match[1].str, mcx.match[1].len);	path2[mcx.match[1].len] = 0;#endif	(void)cbdata; /* currently unused */	/* According to method */	if (0 == strcmp(ri->request_method, "GET")) {		return ExampleGET(conn, path1, path2);	}	if ((0 == strcmp(ri->request_method, "PUT"))	    || (0 == strcmp(ri->request_method, "POST"))	    || (0 == strcmp(ri->request_method, "PATCH"))) {		/* In this example, do the same for PUT, POST and PATCH */		return ExamplePUT(conn, path1, path2);	}	if (0 == strcmp(ri->request_method, "DELETE")) {		return ExampleDELETE(conn, path1, path2);	}	/* this is not a GET request */	mg_send_http_error(	    conn, 405, "Only GET, PUT, POST, DELETE and PATCH method supported");	return 405;}static intExitHandler(struct mg_connection *conn, void *cbdata){	mg_printf(conn,	          "HTTP/1.1 200 OK\r\nContent-Type: "	          "text/plain\r\nConnection: close\r\n\r\n");	mg_printf(conn, "Server will shut down.\n");	mg_printf(conn, "Bye!\n");	exitNow = 1;	return 1;}static intlog_message(const struct mg_connection *conn, const char *message){	puts(message);	return 1;}intmain(int argc, char *argv[]){	const char *options[] = {"listening_ports",	                         PORT,	                         "request_timeout_ms",	                         "10000",	                         "error_log_file",	                         "error.log",	                         0};	struct mg_callbacks callbacks;	struct mg_context *ctx;	int err = 0;	/* Init libcivetweb. */	mg_init_library(0);	/* Callback will print error messages to console */	memset(&callbacks, 0, sizeof(callbacks));	callbacks.log_message = log_message;	/* Start CivetWeb web server */	ctx = mg_start(&callbacks, 0, options);	/* Check return value: */	if (ctx == NULL) {		fprintf(stderr, "Cannot start CivetWeb - mg_start failed.\n");		return EXIT_FAILURE;	}	/* Add handler EXAMPLE_URI, to explain the example */	mg_set_request_handler(ctx, EXAMPLE_URI, ExampleHandler, 0);	mg_set_request_handler(ctx, EXIT_URI, ExitHandler, 0);	/* Show some info */	printf("Start example: %s%s\n", HOST_INFO, EXAMPLE_URI);	printf("Exit example:  %s%s\n", HOST_INFO, EXIT_URI);	/* Wait until the server should be closed */	while (!exitNow) {#ifdef _WIN32		Sleep(1000);#else		sleep(1);#endif	}	/* Stop the server */	mg_stop(ctx);	printf("Server stopped.\n");	printf("Bye!\n");	return EXIT_SUCCESS;}
 |