|  | @@ -137,14 +137,20 @@ static const char *http_dos_date(uint32_t dos_date)
 | 
	
		
			
				|  |  |  static const char text_plain[] = "text/plain; charset=\"UTF-8\"";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  enum hsp_flags {
 | 
	
		
			
				|  |  | -    HSP_CLOSE = 1,
 | 
	
		
			
				|  |  | -    HSP_CRLF  = 2
 | 
	
		
			
				|  |  | +    HSP_CLOSE        = 1,
 | 
	
		
			
				|  |  | +    HSP_CRLF         = 2,
 | 
	
		
			
				|  |  | +    HSP_CLOSE_SOCKET = 4
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void httpd_print_request(const httpd_req_t *req)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    printf("[HTTP] %s %s\n", http_method_str(req->method), req->uri);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static esp_err_t httpd_send_plain(httpd_req_t *req,
 | 
	
		
			
				|  |  |  				  unsigned int rcode,
 | 
	
		
			
				|  |  |  				  const char *body, size_t blen,
 | 
	
		
			
				|  |  | -				  enum hsp_flags flags)
 | 
	
		
			
				|  |  | +				  enum hsp_flags flags, unsigned int refresh)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      char *header = NULL;
 | 
	
		
			
				|  |  |      esp_err_t err;
 | 
	
	
		
			
				|  | @@ -157,6 +163,12 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
 | 
	
		
			
				|  |  |      const char *closer = flags & HSP_CLOSE ? "Connection: close\r\n" : "";
 | 
	
		
			
				|  |  |      bool redirect = rcode >= 300 && rcode <= 399;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    static const char refresher[] = "Refresh: %u;url=/";
 | 
	
		
			
				|  |  | +    char refresh_str[sizeof(refresher)-2 + sizeof(unsigned int)*3];
 | 
	
		
			
				|  |  | +    refresh_str[0] = '\0';
 | 
	
		
			
				|  |  | +    if (refresh)
 | 
	
		
			
				|  |  | +	snprintf(refresh_str, sizeof refresh_str, refresher, refresh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      if (redirect) {
 | 
	
		
			
				|  |  |  	size_t blenadj = sizeof("3xx Redirect \r"); /* \0 -> \n so don't include it */
 | 
	
		
			
				|  |  |  	flags |= HSP_CRLF;
 | 
	
	
		
			
				|  | @@ -167,11 +179,11 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
 | 
	
		
			
				|  |  |  			"Content-Length: %zu\r\n"
 | 
	
		
			
				|  |  |  			"Date %s\r\n"
 | 
	
		
			
				|  |  |  			"Location: %.*s\r\n"
 | 
	
		
			
				|  |  | -			"%s"
 | 
	
		
			
				|  |  | +			"%s%s"
 | 
	
		
			
				|  |  |  			"\r\n"
 | 
	
		
			
				|  |  |  			"%3u Redirect ",
 | 
	
		
			
				|  |  |  			rcode, text_plain, blen + blenadj,
 | 
	
		
			
				|  |  | -			now, (int)blen, body, closer, rcode);
 | 
	
		
			
				|  |  | +			now, (int)blen, body, closer, refresh_str, rcode);
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  |  	size_t blenadj = (flags & HSP_CRLF) ? 2 : 0;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -179,10 +191,12 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
 | 
	
		
			
				|  |  |  			"HTTP/1.1 %u\r\n"
 | 
	
		
			
				|  |  |  			"Content-Type: %s\r\n"
 | 
	
		
			
				|  |  |  			"Content-Length: %zu\r\n"
 | 
	
		
			
				|  |  | +			"Cache-Control: no-store\r\n"
 | 
	
		
			
				|  |  |  			"Date: %s\r\n"
 | 
	
		
			
				|  |  | -			"%s"
 | 
	
		
			
				|  |  | +			"%s%s"
 | 
	
		
			
				|  |  |  			"\r\n",
 | 
	
		
			
				|  |  | -			rcode, text_plain, blen + blenadj, now, closer);
 | 
	
		
			
				|  |  | +			rcode, text_plain, blen + blenadj, now,
 | 
	
		
			
				|  |  | +			closer, refresh_str);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (!header)
 | 
	
	
		
			
				|  | @@ -199,12 +213,13 @@ static esp_err_t httpd_send_plain(httpd_req_t *req,
 | 
	
		
			
				|  |  |      if (header)
 | 
	
		
			
				|  |  |  	free(header);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return err;
 | 
	
		
			
				|  |  | +    /* Sending ESP_FAIL causes the socket to be immediately closed */
 | 
	
		
			
				|  |  | +    return err ? err : (flags & HSP_CLOSE_SOCKET) ? ESP_FAIL : ESP_OK;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), s, sizeof(s)-1, HSP_CRLF)
 | 
	
		
			
				|  |  | +#define HTTP_ERR(r,e,s) httpd_send_plain((r), (e), s, sizeof(s)-1, HSP_CRLF, 0)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static esp_err_t httpd_err_404(httpd_req_t *req)
 | 
	
		
			
				|  |  | +static esp_err_t httpd_err_enoent(httpd_req_t *req)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      return HTTP_ERR(req, 404, "URI not found");
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -214,6 +229,11 @@ static esp_err_t httpd_send_ok(httpd_req_t *req)
 | 
	
		
			
				|  |  |      return HTTP_ERR(req, 200, "OK");
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static esp_err_t httpd_err_enomem(httpd_req_t *req)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    return HTTP_ERR(req, 503, "Out of memory");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static esp_err_t httpd_update_done(httpd_req_t *req, const char *what, int err)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      char *response = NULL;
 | 
	
	
		
			
				|  | @@ -236,7 +256,8 @@ static esp_err_t httpd_update_done(httpd_req_t *req, const char *what, int err)
 | 
	
		
			
				|  |  |  	len = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      esp_err_t rv = httpd_send_plain(req, err ? 400 : 200, response, len,
 | 
	
		
			
				|  |  | -				    HSP_CLOSE|HSP_CRLF);
 | 
	
		
			
				|  |  | +				    HSP_CLOSE|HSP_CLOSE_SOCKET|HSP_CRLF,
 | 
	
		
			
				|  |  | +				    reboot_time+5);
 | 
	
		
			
				|  |  |      if (response)
 | 
	
		
			
				|  |  |  	free(response);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -252,40 +273,69 @@ static esp_err_t httpd_firmware_update(httpd_req_t *req)
 | 
	
		
			
				|  |  |      return httpd_update_done(req, "Firmware", rv);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static esp_err_t httpd_set_config(httpd_req_t *req)
 | 
	
		
			
				|  |  | +static esp_err_t httpd_set_config(httpd_req_t *req, const char *query)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -    FILE *f = httpd_fopen_read(req);
 | 
	
		
			
				|  |  | -    if (!f)
 | 
	
		
			
				|  |  | -	return HTTP_ERR(req, 500, "Unable to get request handle");
 | 
	
		
			
				|  |  | +    FILE *f;
 | 
	
		
			
				|  |  | +    size_t qlen;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    int rv = read_config(f);
 | 
	
		
			
				|  |  | -    fclose(f);
 | 
	
		
			
				|  |  | -    if (rv) {
 | 
	
		
			
				|  |  | -	rv = FWUPDATE_ERR_CONFIG_READ;
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -	rv = save_config();
 | 
	
		
			
				|  |  | -	if (rv)
 | 
	
		
			
				|  |  | -	    rv = FWUPDATE_ERR_CONFIG_SAVE;
 | 
	
		
			
				|  |  | +    int rv1 = 0;
 | 
	
		
			
				|  |  | +    if (query) {
 | 
	
		
			
				|  |  | +	rv1 = set_config_url_string(query);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    int rv2 = 0;
 | 
	
		
			
				|  |  | +    f = NULL;
 | 
	
		
			
				|  |  | +    if (req->content_len) {
 | 
	
		
			
				|  |  | +	f = httpd_fopen_read(req);
 | 
	
		
			
				|  |  | +	if (!f)
 | 
	
		
			
				|  |  | +	    return HTTP_ERR(req, 500, "Unable to get request handle");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    rv2 = read_config(f, true);
 | 
	
		
			
				|  |  | +    if (f)
 | 
	
		
			
				|  |  | +	fclose(f);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return httpd_update_done(req, "Configuration", rv);
 | 
	
		
			
				|  |  | +    return httpd_update_done(req, "Configuration", rv1 ? rv1 : rv2);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static esp_err_t httpd_set_lang(httpd_req_t *req, const char *query)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    if (query && *query) {
 | 
	
		
			
				|  |  | +	setenv_config("LANG", query);
 | 
	
		
			
				|  |  | +	read_config(NULL, true);	/* Save configuration */
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return httpd_send_plain(req, 302, "/", 1, 0, 0);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define STRING_MATCHES(str, len, what) \
 | 
	
		
			
				|  |  | +    (((len) == sizeof(what)-1) && !memcmp((str), (what), sizeof(what)-1))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static esp_err_t httpd_sys_post_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -    printf("[POST] len = %zu uri = \"%s\"\n",
 | 
	
		
			
				|  |  | -	   req->content_len, req->uri);
 | 
	
		
			
				|  |  | +    httpd_print_request(req);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!req->content_len)
 | 
	
		
			
				|  |  | +    if (!httpd_req_get_hdr_value_len(req, "Content-Length"))
 | 
	
		
			
				|  |  |  	return HTTP_ERR(req, 411, "Length required");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!strcmp(req->uri, "/sys/fwupdate"))
 | 
	
		
			
				|  |  | +    if (strncmp(req->uri, "/sys/", 5))
 | 
	
		
			
				|  |  | +	return httpd_err_enoent(req); /* This should never happen */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    const char *file = req->uri + 5;
 | 
	
		
			
				|  |  | +    size_t filelen = strcspn(file, "?");
 | 
	
		
			
				|  |  | +    const char *query = NULL;
 | 
	
		
			
				|  |  | +    if (file[filelen] == '?')
 | 
	
		
			
				|  |  | +	query = &file[filelen] + 1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (STRING_MATCHES(file, filelen, "fwupdate"))
 | 
	
		
			
				|  |  |  	return httpd_firmware_update(req);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (!strcmp(req->uri, "/sys/setconfig"))
 | 
	
		
			
				|  |  | -	return httpd_set_config(req);
 | 
	
		
			
				|  |  | +    if (STRING_MATCHES(file, filelen, "setconfig"))
 | 
	
		
			
				|  |  | +	return httpd_set_config(req, query);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (STRING_MATCHES(file, filelen, "lang"))
 | 
	
		
			
				|  |  | +	return httpd_set_lang(req, query);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return httpd_err_404(req);
 | 
	
		
			
				|  |  | +    return httpd_err_enoent(req);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static esp_err_t httpd_get_config(httpd_req_t *req)
 | 
	
	
		
			
				|  | @@ -295,6 +345,7 @@ static esp_err_t httpd_get_config(httpd_req_t *req)
 | 
	
		
			
				|  |  |  	return HTTP_ERR(req, 500, "Unable to get request handle");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      httpd_resp_set_type(req, text_plain);
 | 
	
		
			
				|  |  | +    httpd_resp_set_hdr(req, "Cache-Control", "no-store");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      int rv = write_config(f);
 | 
	
		
			
				|  |  |      fclose(f);
 | 
	
	
		
			
				|  | @@ -303,10 +354,12 @@ static esp_err_t httpd_get_config(httpd_req_t *req)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static esp_err_t httpd_sys_get_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +    httpd_print_request(req);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      if (!strcmp(req->uri, "/sys/getconfig"))
 | 
	
		
			
				|  |  |  	return httpd_get_config(req);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    return httpd_err_404(req);
 | 
	
		
			
				|  |  | +    return httpd_err_enoent(req);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  INCBIN_EXTERN(wwwzip);
 | 
	
	
		
			
				|  | @@ -346,7 +399,7 @@ static const struct mime_type mime_types[] = {
 | 
	
		
			
				|  |  |  static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      static const char index_filename[] = "index.html";
 | 
	
		
			
				|  |  | -    static const char default_lang[] = "en";
 | 
	
		
			
				|  |  | +    static const char fallback_lang[] = "en";
 | 
	
		
			
				|  |  |      size_t buffer_size = UNZ_BUFSIZE;
 | 
	
		
			
				|  |  |      const char *uri, *enduri;
 | 
	
		
			
				|  |  |      bool add_index;
 | 
	
	
		
			
				|  | @@ -357,6 +410,8 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |      int err = 0;
 | 
	
		
			
				|  |  |      size_t len;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    httpd_print_request(req);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      uri = req->uri;
 | 
	
		
			
				|  |  |      while (*uri == '/')
 | 
	
		
			
				|  |  |  	uri++;			/* Skip leading slashes */
 | 
	
	
		
			
				|  | @@ -381,16 +436,10 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |  	add_index = false;	/* Try the plain filename first */
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    MSG("requesting: /%.*s\n", enduri - uri, uri);
 | 
	
		
			
				|  |  | -    if (first_slash)
 | 
	
		
			
				|  |  | -	MSG("first_slash = %.*s\n", enduri - first_slash, first_slash);
 | 
	
		
			
				|  |  | -    else
 | 
	
		
			
				|  |  | -	MSG("first_slash = NULL\n");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    const char *lang = getenv("LANG");
 | 
	
		
			
				|  |  | +    const char *lang = getenv_def("LANG", "");
 | 
	
		
			
				|  |  |      const size_t lang_size = lang ? strlen(lang)+1 : 0;
 | 
	
		
			
				|  |  | -    const size_t lang_space = (lang_size < sizeof default_lang
 | 
	
		
			
				|  |  | -			       ? sizeof default_lang : lang_size) + 1;
 | 
	
		
			
				|  |  | +    const size_t lang_space = (lang_size < sizeof fallback_lang
 | 
	
		
			
				|  |  | +			       ? sizeof fallback_lang : lang_size) + 1;
 | 
	
		
			
				|  |  |      const size_t filename_buffer_size =
 | 
	
		
			
				|  |  |  	(enduri - uri) + lang_space + 2 + sizeof index_filename;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -400,7 +449,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |      buffer = malloc(buffer_size);
 | 
	
		
			
				|  |  |      zip = malloc(sizeof *zip);
 | 
	
		
			
				|  |  |      if (!buffer || !zip) {
 | 
	
		
			
				|  |  | -	err = HTTP_ERR(req, 503, "Out of memory");
 | 
	
		
			
				|  |  | +	err = httpd_err_enomem(req);
 | 
	
		
			
				|  |  |  	goto out;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -440,8 +489,8 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |  	    }
 | 
	
		
			
				|  |  |  	    break;
 | 
	
		
			
				|  |  |  	case 2:
 | 
	
		
			
				|  |  | -	    filename = filebase - sizeof default_lang;
 | 
	
		
			
				|  |  | -	    memcpy(filename, default_lang, sizeof default_lang - 1);
 | 
	
		
			
				|  |  | +	    filename = filebase - sizeof fallback_lang;
 | 
	
		
			
				|  |  | +	    memcpy(filename, fallback_lang, sizeof fallback_lang - 1);
 | 
	
		
			
				|  |  |  	    break;
 | 
	
		
			
				|  |  |  	default:
 | 
	
		
			
				|  |  |  	    filename = filebase;
 | 
	
	
		
			
				|  | @@ -462,10 +511,10 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |      size_t filelen = endfile - filename;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (!found) {
 | 
	
		
			
				|  |  | -	err = httpd_err_404(req);
 | 
	
		
			
				|  |  | +	err = httpd_err_enoent(req);
 | 
	
		
			
				|  |  |  	goto out;
 | 
	
		
			
				|  |  |      } else if (m) {
 | 
	
		
			
				|  |  | -	err = httpd_send_plain(req, 302 - (m == 1), filename-1, filelen+1, 0);
 | 
	
		
			
				|  |  | +	err = httpd_send_plain(req, 302 - (m == 1), filename-1, filelen+1, 0, 0);
 | 
	
		
			
				|  |  |  	goto out;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -508,6 +557,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |      len = snprintf(buffer, buffer_size-2,
 | 
	
		
			
				|  |  |  		   "HTTP/1.1 %s\r\n"
 | 
	
		
			
				|  |  |  		   "Date: %s\r\n"
 | 
	
		
			
				|  |  | +		   "Cache-Control: max-age=10\r\n"
 | 
	
		
			
				|  |  |  		   "ETag: %s\r\n",
 | 
	
		
			
				|  |  |  		   response,
 | 
	
		
			
				|  |  |  		   http_now(),
 | 
	
	
		
			
				|  | @@ -521,6 +571,7 @@ static esp_err_t httpd_static_handler(httpd_req_t *req)
 | 
	
		
			
				|  |  |  			"Content-Type: %s%s\r\n"
 | 
	
		
			
				|  |  |  			"Content-Length: %u\r\n"
 | 
	
		
			
				|  |  |  			"Allow: GET, HEAD\r\n"
 | 
	
		
			
				|  |  | +			"Connection: close\r\n"
 | 
	
		
			
				|  |  |  			"Last-Modified: %s\r\n",
 | 
	
		
			
				|  |  |  			mime_type->mime, mime_extra,
 | 
	
		
			
				|  |  |  			fileinfo.uncompressed_size,
 | 
	
	
		
			
				|  | @@ -645,6 +696,7 @@ static const httpd_uri_t uri_handlers[] = {
 | 
	
		
			
				|  |  |  void my_httpd_stop(void)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      if (httpd) {
 | 
	
		
			
				|  |  | +	esp_unregister_shutdown_handler(my_httpd_stop);
 | 
	
		
			
				|  |  |  	httpd_stop(httpd);
 | 
	
		
			
				|  |  |  	httpd = NULL;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -670,6 +722,8 @@ void my_httpd_start(void)
 | 
	
		
			
				|  |  |      if (httpd_start(&server, &config) != ESP_OK)
 | 
	
		
			
				|  |  |        return;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    esp_register_shutdown_handler(my_httpd_stop);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      httpd = server;
 | 
	
		
			
				|  |  |      for (size_t i = 0; i < ARRAY_SIZE(uri_handlers); i++) {
 | 
	
		
			
				|  |  |  	const httpd_uri_t * const handler = &uri_handlers[i];
 |