Add initial version of the Kestrel Web service

Framework now in place for further expansion
parent 76938bb4
......@@ -5,6 +5,7 @@ SET(FPGA_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bootrom/fpga/release"
"Path to the FPGA build folder from the litex-boards repo: <litex-boards-repo>/litex_boards/targets/build/versa_ecp5")
SET(KESTREL_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/kestrel)
SET(CIVETWEB_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/civetweb)
SET(EMBEDDED_FILE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/embeddedfiles)
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
......@@ -35,4 +36,27 @@ target_sources(app PRIVATE
${KESTREL_SOURCE_DIR}/src/webservice.c
${KESTREL_SOURCE_DIR}/src/direct_uart.c
${CIVETWEB_SOURCE_DIR}/src/libc_extensions.c
${CIVETWEB_SOURCE_DIR}/src/raptor_extensions.c
)
SET(embedded_file_sources
${EMBEDDED_FILE_SOURCE_DIR}/index.html
${EMBEDDED_FILE_SOURCE_DIR}/header.inc
${EMBEDDED_FILE_SOURCE_DIR}/footer.inc
${EMBEDDED_FILE_SOURCE_DIR}/style.css
${EMBEDDED_FILE_SOURCE_DIR}/raptor_logo.jpg
${EMBEDDED_FILE_SOURCE_DIR}/kestrel_avatar_small.png
)
SET(embedded_file_sources_list "")
foreach(file ${embedded_file_sources})
string(APPEND embedded_file_sources_list ${file} "\;")
endforeach()
SET(static_file_header ${KESTREL_SOURCE_DIR}/src/static_files.h)
add_custom_target(
static_files ALL
COMMAND COMMAND ${CMAKE_COMMAND} -D KESTREL_SOURCE_DIR="${KESTREL_SOURCE_DIR}" -D embedded_file_sources="${embedded_file_sources_list}" -P ${CMAKE_CURRENT_SOURCE_DIR}/generate_static_files.cmake
DEPENDS ${embedded_file_sources}
COMMENT "Generating C header file from static files"
)
\ No newline at end of file
# Copyright 2020 Sivachandran Paramasivam
# Licensed under the terms of the MIT license
include(CMakeParseArguments)
# Function to wrap a given string into multiple lines at the given column position.
# Parameters:
# VARIABLE - The name of the CMake variable holding the string.
# AT_COLUMN - The column position at which string will be wrapped.
function(WRAP_STRING)
set(oneValueArgs VARIABLE AT_COLUMN)
cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN})
string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength)
math(EXPR offset "0")
while(stringLength GREATER 0)
if(stringLength GREATER ${WRAP_STRING_AT_COLUMN})
math(EXPR length "${WRAP_STRING_AT_COLUMN}")
else()
math(EXPR length "${stringLength}")
endif()
string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line)
set(lines "${lines}\n${line}")
math(EXPR stringLength "${stringLength} - ${length}")
math(EXPR offset "${offset} + ${length}")
endwhile()
set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE)
endfunction()
# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file
# will contain a byte array and integer variable holding the size of the array.
# Parameters
# SOURCE_FILE - The path of source file whose contents will be embedded in the header file.
# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append
# to this name and will be used a variable name for size variable.
# HEADER_FILE - The path of header file.
# APPEND - If specified appends to the header file instead of overwriting it
# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be
# useful if the source file is a text file and we want to use the file contents
# as string. But the size variable holds size of the byte array without this
# null byte.
# Usage:
# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG")
function(BIN2H)
set(options APPEND NULL_TERMINATE)
set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE)
cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN})
# reads source file contents as hex string
file(READ ${BIN2H_SOURCE_FILE} hexString HEX)
string(LENGTH ${hexString} hexStringLength)
# appends null byte if asked
if(BIN2H_NULL_TERMINATE)
set(hexString "${hexString}00")
endif()
# wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line)
wrap_string(VARIABLE hexString AT_COLUMN 32)
math(EXPR arraySize "${hexStringLength} / 2")
# adds '0x' prefix and comma suffix before and after every byte respectively
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString})
# removes trailing comma
string(REGEX REPLACE ", $" "" arrayValues ${arrayValues})
# converts the variable name into proper C identifier
string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
string(TOLOWER "embedded_file_${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
# declares byte array and the length variables
set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };")
set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};")
set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n")
if(BIN2H_APPEND)
file(APPEND ${BIN2H_HEADER_FILE} "${declarations}")
else()
file(WRITE ${BIN2H_HEADER_FILE} "${declarations}")
endif()
endfunction()
\ No newline at end of file
bootrom @ 5573c718
Subproject commit 399db4c68f98c326d07e2e842980ae8741bfd5a4
Subproject commit 5573c7184997d0d3e18b8df87c0ee9e1fbfed3cf
// © 2021 Raptor Engineering, LLC
//
// Released under the terms of the GPL v3
// See the LICENSE file for full details
#ifndef _RAPTOR_EXTENSIONS
#define _RAPTOR_EXTENSIONS
void civetweb_send_file_data(struct mg_connection *conn, const uint8_t* data, int64_t len);
void civetweb_send_file(struct mg_connection *conn, const char* content_type, const uint8_t* data, size_t length);
#endif // _RAPTOR_EXTENSIONS
\ No newline at end of file
/*
* Copyright (c) 2019 Antmicro Ltd
* Copyright (c) 2021 Raptor Engineering, LLC <sales@raptorengineering.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
......@@ -30,6 +31,9 @@ size_t strftime(char *dst, size_t dst_size,
{
FN_MISSING();
// Prevent garbage from being transmitted
memset(dst, 0, dst_size);
return 0;
}
......
/* Copyright (c) 2013-2021 the Civetweb developers
* Copyright (c) 2004-2013 Sergey Lyubka
* Copyright (c) 2021 Raptor Engineering, LLC <sales@raptorengineering.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include <zephyr.h>
#include <posix/pthread.h>
#include "libc_extensions.h"
#include "civetweb.h"
/* General purpose buffer size. */
#if !defined(MG_BUF_LEN) /* in bytes */
#define MG_BUF_LEN (1024 * 8)
#endif
/* Return null terminated string of given maximum length.
* Report errors if length is exceeded. */
static void
mg_vsnprintf(const struct mg_connection *conn,
int *truncated,
char *buf,
size_t buflen,
const char *fmt,
va_list ap)
{
int n, ok;
if (buflen == 0) {
if (truncated) {
*truncated = 1;
}
return;
}
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
/* Using fmt as a non-literal is intended here, since it is mostly called
* indirectly by mg_snprintf */
#endif
n = (int)vsnprintf(buf, buflen, fmt, ap);
ok = (n >= 0) && ((size_t)n < buflen);
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
if (ok) {
if (truncated) {
*truncated = 0;
}
} else {
if (truncated) {
*truncated = 1;
}
n = (int)buflen - 1;
}
buf[n] = '\0';
}
static void
mg_snprintf(const struct mg_connection *conn,
int *truncated,
char *buf,
size_t buflen,
const char *fmt,
...)
{
va_list ap;
va_start(ap, fmt);
mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
va_end(ap);
}
static void
mg_strlcpy(char *dst, const char *src, size_t n)
{
for (; *src != '\0' && n > 1; n--) {
*dst++ = *src++;
}
*dst = '\0';
}
/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
* included in all responses other than 100, 101, 5xx. */
static void
gmt_time_string(char *buf, size_t buf_len, time_t *t)
{
#if !defined(REENTRANT_TIME)
struct tm *tm;
tm = ((t != NULL) ? gmtime(t) : NULL);
if (tm != NULL) {
#else
struct tm _tm;
struct tm *tm = &_tm;
if (t != NULL) {
gmtime_r(t, tm);
#endif
strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
} else {
mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
buf[buf_len - 1] = '\0';
}
}
static int parse_range_header(const char *header, int64_t *a, int64_t *b)
{
return sscanf(header, "bytes=%lld-%lld", a, b);
}
void civetweb_send_file_data(struct mg_connection *conn, const uint8_t* data, int64_t len)
{
char buf[MG_BUF_LEN];
int to_read, num_written;
if (!data || !conn) {
return;
}
while (len > 0) {
/* Calculate how much to read from the file in the buffer */
to_read = sizeof(buf);
if ((int64_t)to_read > len) {
to_read = (int)len;
}
/* Send read bytes to the client, exit the loop on error */
if ((num_written = mg_write(conn, data, (size_t)to_read))
!= to_read) {
break;
}
data += num_written;
/* Both read and were successful, adjust counters */
len -= num_written;
}
}
void civetweb_send_file(struct mg_connection *conn, const char* content_type, const uint8_t* data, size_t length)
{
char lm[64], etag[64];
char range[128]; /* large enough, so there will be no overflow */
const char *range_hdr;
int64_t cl, r1, r2;
int n, truncated;
const char *encoding = 0;
const char *origin_hdr;
const char *cors_orig_cfg;
const char *cors1, *cors2;
int status_code;
int is_head_request;
// FIXME
// Set to some future date for images to avoid constant reloads from the overtaxed Kestrel web server...
uint64_t last_modified = 0;
cl = (int64_t)length;
status_code = 200;
range[0] = '\0';
/* Check if there is a range header */
range_hdr = mg_get_header(conn, "Range");
/* If "Range" request was made: parse header, send only selected part
* of the file. */
r1 = r2 = 0;
if ((range_hdr != NULL)
&& ((n = parse_range_header(range_hdr, &r1, &r2)) > 0) && (r1 >= 0)
&& (r2 >= 0)) {
status_code = 206;
cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1);
mg_snprintf(conn,
NULL, /* range buffer is big enough */
range,
sizeof(range),
"bytes "
"%lld-%lld/%lld",
r1,
r1 + cl - 1,
length);
}
/* Standard CORS header */
origin_hdr = mg_get_header(conn, "Origin");
cors1 = "Access-Control-Allow-Origin";
cors2 = cors_orig_cfg;
/* Prepare Etag, and Last-Modified headers. */
gmt_time_string(lm, sizeof(lm), &last_modified);
mg_snprintf(NULL,
NULL, /* All calls to construct_etag use 64 byte buffer */
etag,
sizeof(etag),
"\"%lx.%lld\"",
(unsigned long)last_modified,
length);
/* Create 2xx (200, 206) response */
mg_response_header_start(conn, status_code);
mg_response_header_add(conn, "Cache-Control", "600", -1);
//send_additional_header(conn);
mg_response_header_add(conn,
"Content-Type",
content_type,
(int)strlen(content_type));
if (cors1[0] != 0) {
mg_response_header_add(conn, cors1, cors2, -1);
}
mg_response_header_add(conn, "Last-Modified", lm, -1);
mg_response_header_add(conn, "Etag", etag, -1);
/* Without on-the-fly compression, we know the content-length
* and we can use ranges (with on-the-fly compression we cannot).
* So we send these response headers only in this case. */
char len[32];
int trunc = 0;
mg_snprintf(conn, &trunc, len, sizeof(len), "%lld", cl);
if (!trunc) {
mg_response_header_add(conn, "Content-Length", len, -1);
}
mg_response_header_add(conn, "Accept-Ranges", "bytes", -1);
if (encoding) {
mg_response_header_add(conn, "Content-Encoding", encoding, -1);
}
if (range[0] != 0) {
mg_response_header_add(conn, "Content-Range", range, -1);
}
/* Send all headers */
mg_response_header_send(conn);
if (!is_head_request) {
civetweb_send_file_data(conn, data + r1, cl);
}
}
\ No newline at end of file
</div>
<div class="footer">
<table width=100%>
<tr>
<td width=100% align=center>
<table class="footer_text">
<tr>
<td rowspan=2>
<a href="https://www.raptorengineering.com"><img src="raptor_logo.jpg" height="30" border="0"></a>
</td>
<td>
Kestrel brought to you by <a href="https://www.raptorengineering.com">Raptor Engineering</a>
</td>
</tr>
<tr>
<td>
Get the <a href="https://gitlab.raptorengineering.com/kestrel-collaboration">source code</a> and collaborate on our GitLab server today!
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</body>
</html>
<body>
<img src="kestrel_avatar_small.png" height="100" border="0" style="float:left; position:absolute; top: 0px; z-index: 10;">
<div class="sidebar_nav">
<a href="/info">Info</a>
<a href="/firmwareupload$">Firmware</a>
</div>
<div class="header">
Kestrel SoftBMC
</div>
<div class="main">
<h3>Welcome to the Kestrel web management portal!</h3>
Available actions:
<ul>
<li><a href=/info>System information</a></li>
<li><a href=/firmwareupload>Firmware upload</a></li>
</ul>
include(${KESTREL_SOURCE_DIR}/../bin2hex.cmake)
SET(static_file_header ${KESTREL_SOURCE_DIR}/src/static_files.h)
file(WRITE ${static_file_header} "// © 2021 Raptor Engineering, LLC\n//\n// Released under the terms of the GPL v3\n// See the LICENSE file for full details\n\n#ifndef _STATIC_FILES\n#define _STATIC_FILES\n\n")
foreach(file ${embedded_file_sources})
get_filename_component(variableName ${file} NAME)
bin2h(SOURCE_FILE ${file} HEADER_FILE ${static_file_header} VARIABLE_NAME ${variableName} APPEND NULL_TERMINATE)
file(APPEND ${static_file_header} "\n")
endforeach()
file(APPEND ${static_file_header} "\n\n#endif // _STATIC_FILES\n")
\ No newline at end of file
......@@ -37,6 +37,7 @@ struct firmware_buffer_region {
uint8_t *buffer_address;
unsigned long long buffer_length;
uintptr_t current_write_offset;
unsigned long long valid_bytes;
uint8_t locked;
uint8_t overflow;
};
......
/*
* Copyright (c) 2019 Antmicro Ltd
* Copyright (c) 2021 Raptor Engineering, LLC <sales@raptorengineering.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
......@@ -13,6 +14,9 @@
#define WITH_ZEPHYR 1
#include "kestrel.h"
#include "raptor_extensions.h"
#include "static_files.h"
#define HTTP_PORT 8080
#define HTTPS_PORT 4443
......@@ -21,8 +25,52 @@
/* Use samllest possible value of 1024 (see the line 18619 of civetweb.c) */
#define MAX_REQUEST_SIZE_BYTES 1024
#define STANDARD_HEADER(title) \
"<head>" \
"<link rel=\"stylesheet\" href=\"style.css\"><meta charset=\"UTF-8\">" \
"<title>" title "</title>" \
"</head>"
#define SEND_STANDARD_TITLE_HEADER(conn, title) \
civetweb_send_file_data(conn, STANDARD_HEADER(title), sizeof(STANDARD_HEADER(title)));
#define SEND_STANDARD_HEADER(conn, title) \
SEND_STANDARD_TITLE_HEADER(conn, title) \
civetweb_send_file_data(conn, embedded_file_header_inc, sizeof(embedded_file_header_inc)-1);
#define SEND_STANDARD_FOOTER(conn) \
civetweb_send_file_data(conn, embedded_file_footer_inc, sizeof(embedded_file_footer_inc)-1);
#define KESTREL_WEBSERVICE_DEFINE_FILE_HANDLER(name, type) \
static int static_file_##name##_handler(struct mg_connection *conn, void *cbdata) \
{ \
civetweb_send_file(conn, type, embedded_file_##name, sizeof(embedded_file_##name)-1); \
return 200; \
}
#define KESTREL_WEBSERVICE_DEFINE_PAGE_HANDLER(name, type, title) \
static int static_file_##name##_handler(struct mg_connection *conn, void *cbdata) \
{ \
mg_printf(conn, \
"HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Connection: close\r\n\r\n"); \
SEND_STANDARD_HEADER(conn, title) \
civetweb_send_file_data(conn, embedded_file_##name, sizeof(embedded_file_##name)-1); \
SEND_STANDARD_FOOTER(conn) \
return 200; \
}
#define KESTREL_WEBSERVICE_REGISTER_FILE_HANDLER(name, path) \
mg_set_request_handler(ctx, path, static_file_##name##_handler, (void *)0);
K_THREAD_STACK_DEFINE(civetweb_stack, CIVETWEB_MAIN_THREAD_STACK_SIZE);
KESTREL_WEBSERVICE_DEFINE_PAGE_HANDLER(index_html, "text/html", "Kestrel SoftBMC")
KESTREL_WEBSERVICE_DEFINE_FILE_HANDLER(style_css, "text/css")
KESTREL_WEBSERVICE_DEFINE_FILE_HANDLER(raptor_logo_jpg, "image/jpeg")
KESTREL_WEBSERVICE_DEFINE_FILE_HANDLER(kestrel_avatar_small_png, "image/png")
struct civetweb_info {
const char *version;
const char *os;
......@@ -48,21 +96,6 @@ void send_ok(struct mg_connection *conn)
"Connection: close\r\n\r\n");
}
int hello_world_handler(struct mg_connection *conn, void *cbdata)
{
send_ok(conn);
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Hello World from Zephyr!</h3>");
mg_printf(conn, "See also:\n");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li><a href=/info>system info</a></li>\n");
mg_printf(conn, "<li><a href=/firmwareupload>firmware upload</a></li>\n");
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
return 200;
}
int system_info_handler(struct mg_connection *conn, void *cbdata)
{
static const struct json_obj_descr descr[] = {
......@@ -90,18 +123,17 @@ int system_info_handler(struct mg_connection *conn, void *cbdata)
return 500;
}
SEND_STANDARD_HEADER(conn, "Kestrel Information")
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Server info</h3>");
mg_printf(conn, "<h3>BMC information</h3>");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li>host os - %s</li>\n", info.os);
mg_printf(conn, "<li>server - civetweb %s</li>\n", info.version);
mg_printf(conn, "<li>compiler - %s</li>\n", info.compiler);
mg_printf(conn, "<li>board - %s</li>\n", CONFIG_BOARD);
mg_printf(conn, "<li>BMC OS - %s</li>\n", info.os);
mg_printf(conn, "<li>Server - civetweb %s</li>\n", info.version);
mg_printf(conn, "<li>Compiler - %s</li>\n", info.compiler);
mg_printf(conn, "<li>Board - %s</li>\n", CONFIG_BOARD);
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
SEND_STANDARD_FOOTER(conn);
return 200;
}
......@@ -112,18 +144,18 @@ int firmware_upload_form_handler(struct mg_connection *conn, void *cbdata)
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: "
"close\r\n\r\n");
mg_printf(conn, "<!DOCTYPE html>\n");
mg_printf(conn, "<html>\n<head>\n");
mg_printf(conn, "<meta charset=\"UTF-8\">\n");
mg_printf(conn, "<title>File upload</title>\n");
mg_printf(conn, "</head>\n<body>\n");
SEND_STANDARD_HEADER(conn, "Firmware Upload")
mg_printf(conn, "<h3>Firmware Upload</h3>");
mg_printf(conn,
"<form action=\"%s\" method=\"POST\" "
"enctype=\"multipart/form-data\">\n",
(const char *)cbdata);
mg_printf(conn, "<input type=\"file\" name=\"firmwarefile\" multiple>\n");
mg_printf(conn, "<input type=\"submit\" value=\"Submit\">\n");
mg_printf(conn, "</form>\n</body>\n</html>\n");
mg_printf(conn, "</form>\n");
SEND_STANDARD_FOOTER(conn);
return 200;
}
......@@ -154,6 +186,7 @@ int fw_data_received(const char *key, const char *value, size_t valuelen, void *
}
memcpy(fw_data->buffer_address + fw_data->current_write_offset, value, valuelen);
fw_data->current_write_offset += valuelen;
fw_data->valid_bytes = fw_data->current_write_offset;
return 0;
}
......@@ -172,29 +205,36 @@ int firmware_upload_handler(struct mg_connection *conn, void *cbdata)
* mg_handle_form_request. */
(void)req_info;
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n");
main_firmware_buffer.locked = 1;
main_firmware_buffer.overflow = 0;
main_firmware_buffer.current_write_offset = 0;
main_firmware_buffer.valid_bytes = 0;
/* Call the form handler */
mg_printf(conn, "File(s) uploaded:");
ret = mg_handle_form_request(conn, &fdh);
main_firmware_buffer.locked = 0;
mg_printf(conn, "\r\n%i files\r\n", ret);
mg_printf(conn, "\r\n%ld bytes\r\n", main_firmware_buffer.current_write_offset);
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n\r\n");
SEND_STANDARD_HEADER(conn, "Firmware Uploaded")
mg_printf(conn, "<h3>Firmware Uploaded</h3>");
mg_printf(conn, "<p>");
mg_printf(conn, "File(s) uploaded:<br>");
mg_printf(conn, "%i files<br>", ret);
mg_printf(conn, "%lld bytes<br>", main_firmware_buffer.valid_bytes);
if (main_firmware_buffer.overflow) {
mg_printf(conn, "\r\nWARNING: Data was discarded due to buffer overflow!r\n");
mg_printf(conn, "<p>WARNING: Data was discarded due to buffer overflow!");
SEND_STANDARD_FOOTER(conn);
return 500;
}
SEND_STANDARD_FOOTER(conn);
return 200;
}
......@@ -223,11 +263,19 @@ void *main_pthread(void *arg)
return 0;
}
mg_set_request_handler(ctx, "/$", hello_world_handler, (void *)0);
// Static pages
KESTREL_WEBSERVICE_REGISTER_FILE_HANDLER(index_html, "/$")
KESTREL_WEBSERVICE_REGISTER_FILE_HANDLER(style_css, "/style.css$")
// Static files
KESTREL_WEBSERVICE_REGISTER_FILE_HANDLER(raptor_logo_jpg, "/raptor_logo.jpg$")
KESTREL_WEBSERVICE_REGISTER_FILE_HANDLER(kestrel_avatar_small_png, "/kestrel_avatar_small.png$")
// Dynamic pages
mg_set_request_handler(ctx, "/info$", system_info_handler, (void *)0);
mg_set_request_handler(ctx, "/firmwareupload", firmware_upload_form_handler, (void *)"/firmwareupload.callback");
mg_set_request_handler(ctx, "/firmwareupload.callback", firmware_upload_handler, (void *)0);
mg_set_request_handler(ctx, "/firmwareupload$", firmware_upload_form_handler, (void *)"/firmwareupload.callback");
mg_set_request_handler(ctx, "/firmwareupload.callback$", firmware_upload_handler, (void *)0);
return 0;
}
......
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