418 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
//Зависит от SettingsManager
[System.Serializable]
public class ServerResponse
{
public string message;
public string timestamp;
}
public class WebServer : MonoBehaviour
{
private HttpListener listener;
private bool isRunning = true;
private static byte[] css;
async void Start()
{
SettingsManager.Initialize();//надо будет убирать
LoadCss();
await StartWebServerAsync();
}
async Task StartWebServerAsync()
{
listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/");
//тест
listener.Prefixes.Add("http://fishka:8080/");
// 4. ВСЕ сетевые интерфейсы (самый важный!)
listener.Prefixes.Add("http://*:8080/");
listener.Start();
Debug.Log("Async Web сервер запущен");
while (isRunning)
{
try
{
var context = await listener.GetContextAsync();
_ = Task.Run(() => ProcessRequestAsync(context));
}
catch (System.Exception e)
{
Debug.LogError($"Ошибка: {e.Message}");
}
}
}
async Task ProcessRequestAsync(HttpListenerContext context)
{
try
{
string path = context.Request.Url.LocalPath;
string method = context.Request.HttpMethod;
Debug.Log($"Запрос: {method} {path}");
if (method == "GET" && path == "/")
{
await HandleGetSettingsAsync(context);
}
else if (method == "GET" && path == "/style.css")
{
await HandleCssRequestAsync(context);
}
else if (method == "POST" && path == "/upd")
{
await HandlePostUpdateAsync(context);
}
else
{
SendHtmlResponse(context, "<h1>404 Not Found</h1>", 404);
}
}
catch (Exception ex)
{
Debug.LogError($"Error processing request: {ex.Message}");
SendHtmlResponse(context, $"<h1>Error: {ex.Message}</h1>", 500);
}
}
async Task HandleGetSettingsAsync(HttpListenerContext context)
{
await Task.Run(() =>
{
try
{
Debug.Log("Starting SettingsToHtml...");
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var htmlTask = Task.Run(() =>
SettingsToHtml.GetSettingsPage(SettingsManager.Container.Settings));
//SettingsToHtml.GetSettingsString(SettingsManager.Container.Settings));
if (htmlTask.Wait(TimeSpan.FromSeconds(5)))
{
string htmlContent = htmlTask.Result;
stopwatch.Stop();
Debug.Log($"GetSettingsString выполнился за {stopwatch.ElapsedMilliseconds}ms");
if (string.IsNullOrEmpty(htmlContent))
{
htmlContent = "<h1>Error: Method returned empty string</h1>";
}
SendHtmlResponse(context, htmlContent);
}
else
{
Debug.LogError("GetSettingsString TIMEOUT (5 seconds)!");
SendHtmlResponse(context, "<h1>Error: Settings generation timeout</h1>", 500);
}
}
catch (Exception ex)
{
Debug.LogError($"Error in GetSettingsString: {ex.Message}");
SendHtmlResponse(context, $"<h1>Error: {ex.Message}</h1>", 500);
}
});
}
async Task HandleCssRequestAsync(HttpListenerContext context)
{
context.Response.ContentType = "text/css";
context.Response.ContentLength64 = css.Length;
await context.Response.OutputStream.WriteAsync(css, 0, css.Length);
context.Response.OutputStream.Close();
}
private static void LoadCss()
{
TextAsset cssAsset = Resources.Load<TextAsset>("style");
try
{
print(cssAsset.text);
}
catch (Exception)
{
throw;
}
if (cssAsset != null)
{
css= Encoding.UTF8.GetBytes(cssAsset.text);
Debug.Log("CSS OK");
}
else
{
Debug.LogWarning("CSS NOT OK");
css = new byte[0];
}
}
async Task HandlePostUpdateAsync(HttpListenerContext context)
{
try
{
// Определяем кодировку из Content-Type или используем UTF-8 по умолчанию
Encoding encoding = Encoding.UTF8;
string contentType = context.Request.ContentType?.ToLower() ?? "";
if (contentType.Contains("charset="))
{
try
{
string charset = contentType.Split("charset=")[1].Trim().Split(';')[0];
encoding = Encoding.GetEncoding(charset);
Debug.Log($"Определена кодировка из Content-Type: {charset}");
}
catch
{
encoding = Encoding.UTF8;
Debug.Log($"Не удалось определить кодировку из Content-Type, используем UTF-8");
}
}
// Читаем тело POST-запроса
string requestBody;
using (var reader = new StreamReader(context.Request.InputStream, encoding))
{
requestBody = await reader.ReadToEndAsync();
}
Debug.Log($"Получен POST запрос на /upd");
Debug.Log($"Content-Type: {contentType}");
Debug.Log($"Используемая кодировка: {encoding.WebName}");
Debug.Log($"Тело запроса (сырое): {requestBody}");
// Ручное декодирование данных формы
Debug.Log("\n=== Декодированные данные формы ===");
var decodedValues = new Dictionary<string, string>();
// Также можно использовать ParseQueryString для сравнения
try
{
var formData = System.Web.HttpUtility.ParseQueryString(requestBody, encoding);
Debug.Log("\n=== Данные через ParseQueryString ===");
foreach (string key in formData.AllKeys)
{
if (key != null)
{
string value = formData[key] ?? "";
Debug.Log($" {key} = {value}");
// Если ParseQueryString не декодировал ключ, делаем это вручную
if (key.Contains("%"))
{
string decodedKey = Uri.UnescapeDataString(key);
Debug.Log($" Декодированный ключ: {decodedKey}");
}
}
}
}
catch (Exception parseEx)
{
Debug.LogWarning($"ParseQueryString ошибка: {parseEx.Message}");
}
// Получаем action из декодированных данных
string action = decodedValues.ContainsKey("action") ? decodedValues["action"] :
(decodedValues.ContainsKey("action") ? decodedValues["action"] : null);
Debug.Log($"\nAction: {action}");
// Обработка разных действий
if (!string.IsNullOrEmpty(action))
{
if (action == "save")
{
await HandleSaveAction(decodedValues);
}
else if (action == "reset")
{
await HandleResetAction(decodedValues);
}
else
{
Debug.Log($"Неизвестное действие: {action}");
}
}
else
{
Debug.Log("Action не указан, проверяем наличие отдельных кнопок...");
// Проверяем наличие других кнопок
if (decodedValues.ContainsKey("save") && !string.IsNullOrEmpty(decodedValues["save"]))
{
await HandleSaveAction(decodedValues);
}
else if (decodedValues.ContainsKey("reset") && !string.IsNullOrEmpty(decodedValues["reset"]))
{
await HandleResetAction(decodedValues);
}
}
// Отправляем ответ обратно
string responseHtml = @"
<html>
<head>
<meta charset='UTF-8'>
<title>Обновлено</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.success { color: green; font-weight: bold; }
.info { margin: 20px 0; padding: 10px; background: #f0f0f0; }
</style>
</head>
<body>
<h1 class='success'>✓ Данные успешно получены!</h1>
<div class='info'>
<p>Сервер обработал ваш запрос.</p>
<p>Полученные данные:</p>
<ul>";
// Добавляем список полученных данных в ответ
foreach (var kvp in decodedValues)
{
responseHtml += $"<li><strong>{kvp.Key}</strong>: {kvp.Value}</li>";
}
responseHtml += @"
</ul>
</div>
<a href='/' style='display: inline-block; margin-top: 20px; padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 5px;'>
Вернуться к настройкам
</a>
<script>
// Через 5 секунд возвращаемся назад
setTimeout(function() {
window.location.href = '/';
}, 5000);
</script>
</body>
</html>";
SendHtmlResponse(context, responseHtml);
}
catch (Exception ex)
{
Debug.LogError($"Ошибка обработки POST /upd: {ex.Message}");
Debug.LogError($"StackTrace: {ex.StackTrace}");
SendHtmlResponse(context,
$"<html><head><meta charset='UTF-8'><title>Ошибка</title></head><body>" +
$"<h1 style='color:red;'>Ошибка: {ex.Message}</h1>" +
$"<pre>{ex.StackTrace}</pre>" +
$"<a href='/'>Вернуться назад</a></body></html>",
500);
}
}
// Обновленный HandleSaveAction для работы с Dictionary
async Task HandleSaveAction(Dictionary<string, string> formData)
{
Debug.Log("\n=== Обработка сохранения настроек ===");
foreach (var kvp in formData)
{
string key = kvp.Key;
string value = kvp.Value;
Debug.Log($"Поле формы: '{key}' = '{value}'");
// Пропускаем служебные поля
if (key == "action" || key == "_charset_" || key == "submit")
continue;
// TODO: Здесь логика обновления SettingsManager
// Например:
// var setting = SettingsManager.Container.Settings.FirstOrDefault(s => s.Id == key);
// if (setting != null) { ... }
}
Debug.Log("Сохранение настроек завершено");
await Task.CompletedTask;
}
async Task HandleResetAction(Dictionary<string, string> formData)
{
Debug.Log("\n=== Обработка сброса настроек ===");
// TODO: Реализовать сброс настроек
await Task.CompletedTask;
}
async Task HandleSaveAction(System.Collections.Specialized.NameValueCollection formData)
{
// Здесь обрабатываем сохранение настроек
Debug.Log("Обработка сохранения настроек...");
// Пример: получаем значения из формы и обновляем настройки
foreach (string key in formData.AllKeys)
{
string value = formData[key];
Debug.Log($"Поле формы: {key} = {value}");
// TODO: Обновить SettingsManager.Container.Settings на основе данных формы
}
// TODO: Сохранить настройки
// SettingsManager.Save();
await Task.CompletedTask;
}
async Task HandleResetAction(System.Collections.Specialized.NameValueCollection formData)
{
// Здесь обрабатываем сброс настроек
Debug.Log("Обработка сброса настроек...");
// TODO: Сбросить настройки к значениям по умолчанию
await Task.CompletedTask;
}
void SendHtmlResponse(HttpListenerContext context, string html, int statusCode = 200)
{
try
{
byte[] buffer = Encoding.UTF8.GetBytes(html);
context.Response.StatusCode = statusCode;
context.Response.ContentType = "text/html; charset=utf-8";
context.Response.ContentLength64 = buffer.Length;
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
context.Response.OutputStream.Close();
}
catch (Exception ex)
{
Debug.LogError($"Error sending response: {ex.Message}");
}
}
void OnDestroy()
{
isRunning = false;
listener?.Stop();
listener?.Close();
}
private void OnApplicationQuit()
{
isRunning = false;
listener?.Stop();
listener?.Close();
}
}