Newer
Older
Sunfish / DOC / WEB.md
@Ivan Dominguez Ivan Dominguez 21 hours ago 10 KB 3.0

Web: motor HTTP, interfaz y panel de administración

Diseño previsto; aún no implementado.

Por qué un motor HTTP propio

  • Sin administrador. La 2.0 usaba HttpListener, que en Windows va sobre http.sys: escuchar fuera de localhost exige ser administrador o reservar la URL con netsh (que también lo exige). Cambiar la ruta (un "directorio virtual") no lo evita. Un Socket propio no necesita permisos en Windows con ningún puerto; en Linux y macOS los puertos por debajo de 1024 siguen requiriendo privilegios, por eso los puertos por defecto son altos.
  • Tolerancia con clientes antiguos. Los navegadores de los 90 envían peticiones que los servidores modernos rechazan (saltos de línea solo LF, HTTP/1.0 sin Host, cabeceras poco normales). Con un motor propio se aceptan.
  • Native AOT. Sin ASP.NET Core, sunfish-cli puede ser un binario nativo pequeño.

Alcance del motor

  • Versiones: HTTP/0.9 (GET /ruta sin versión: solo el cuerpo), HTTP/1.0 y HTTP/1.1.
  • Métodos: GET, HEAD, POST (formularios y subidas), PUT (subidas modernas) y los de WebDAV (FTP-Y-WEBDAV.md).
  • Analizador tolerante: acepta CRLF o LF; nombres de cabecera sin distinguir mayúsculas; Host opcional en 1.0.
  • Conexiones: keep-alive en 1.1 y en 1.0 si se pide Connection: keep-alive; si no, se cierra al terminar.
  • Respuestas:
    • Los ficheros llevan siempre Content-Length: los clientes antiguos no conocen chunked, y así muestran el progreso.
    • Las páginas generadas se envían con chunked a clientes 1.1 y cerrando la conexión a los 1.0.
  • Rangos (Range: bytes=, uno solo) para reanudar descargas; If-Modified-Since → 304.
  • Cuerpos: Content-Length o chunked; Expect: 100-continue.
  • Autenticación: HTTP Basic (la soportan hasta Mosaic y los primeros Netscape).
  • Límites:
    • Tamaño de la línea de petición y de las cabeceras (8 KB).
    • Plazos de inactividad (contra conexiones lentas a propósito).
    • Conexiones por IP.
    • Tamaño máximo de subida, opcional.
  • Puerto por defecto: 8080, configurable; IPv4 e IPv6.
  • Sin TLS (fuera de alcance; para eso existe Arrejollal).

Interfaz web

Dos modos con la misma página

Modo Para quién Qué cambia
Clásico Navegadores antiguos (por defecto si el User-Agent no es moderno) HTML 3.2/4.01 Transitional, maquetado con tablas, sin JavaScript, ISO-8859-1
Moderno Navegadores actuales La misma estructura, UTF-8, un CSS sencillo y un JS pequeño para arrastrar y soltar y para subir por trozos con progreso
  • El servidor decide por el User-Agent, dentro de los perfiles de cliente (dos, win16, mac, text, classic, modern) descritos en CLIENTES.md, que además ajustan nombres (8.3, 31 caracteres), juego de caracteres y entrega de ficheros de Mac.
  • Forzar el perfil: ?p=…. El perfil forzado se propaga en los enlaces de la página (algunos clientes antiguos no guardan cookies) y se recuerda en una cookie cuando se puede.
  • Fuente sin serifa en los dos modos (CSS en el moderno, <font face> en el clásico).
  • El modo clásico no incluye ningún <script>: algunos navegadores antiguos muestran un diálogo de error por cada script que no entienden.
  • Todo funciona sin JS: el JS del modo moderno solo mejora lo que ya hacen los formularios.
  • Sin recursos externos (la 2.0 cargaba fuentes e iconos de Internet; en una LAN sin salida se veía rota). Iconos en GIF incrustados: IE3, Netscape 1–3 y Mosaic no muestran PNG.

Juego de caracteres

  • Modo clásico: la página se declara ISO-8859-1.
    • Los caracteres Latin-1 se escriben como entidades HTML (&aacute;), que entienden incluso los navegadores de HTML 2.0.
    • Los demás caracteres se muestran como ? (o transliterados cuando haya equivalente).
  • Modo moderno: UTF-8.
  • Los enlaces llevan siempre los bytes UTF-8 reales en %XX, así que funcionan aunque el texto se vea transliterado: el navegador devuelve los mismos bytes al pedir.
  • Lo que envía un navegador antiguo (nombres de carpeta nueva, nombres en subidas) llega en el juego de caracteres de la página o en el del sistema.
    • El servidor intenta UTF-8 y, si no es válido, usa el juego configurado para clientes antiguos (ISO-8859-1 por defecto; MacRoman para Mac clásico).
    • Pendiente de probar con clientes reales.
  • Tabla con icono, nombre (enlace), tamaño, fecha y acciones. Se ordena por nombre, tamaño o fecha con un enlace en la cabecera (?sort=), sin JS.
  • Migas de pan con la ruta y enlace a la raíz.
  • Formularios, según los permisos del share:
    • subir fichero (multipart/form-data, RFC 1867, desde Netscape 2 e IE3/IE4);
    • crear carpeta;
    • descargar en ZIP.
  • Renombrar y borrar llevan a una página de confirmación con un formulario POST. Nunca hay cambios por GET (la 2.0 borraba con ?action=delete).
  • Mac clásico: los ficheros con resource fork se entregan en MacBinary a los Mac clásicos de forma automática; para los demás clientes, enlaces [.bin] y [.hqx] (ver MAC-CLASICO.md).
  • DOS y Windows 3.1: alias 8.3 en el listado y en las descargas (ver CLIENTES.md).
  • ?$list muestra este navegador aunque la carpeta tenga index.html (si el share tiene Listing). En el menú raíz y en los listados, las carpetas con índice llevan un pequeño enlace [contenido] que apunta a él.
  • Escapado: todo nombre que se escribe en el HTML se escapa (la 2.0 no lo hacía).

Subidas

  • Multipart: el analizador trabaja en flujo y escribe directamente a disco; los límites pueden quedar partidos entre búferes. La 2.0 leía byte a byte y lo guardaba todo en memoria.
  • Nombre del fichero: se queda solo el nombre. IE antiguo envía la ruta completa (C:\WINDOWS\fichero.txt) y Mac clásico puede usar : como separador.
  • Escritura segura: se escribe a un temporal en la misma carpeta y se renombra al terminar. Una subida cortada no deja un fichero a medias ni pisa el existente.
  • Modo moderno: sunfish.js envía cada fichero con PUT a su URL en trozos de 1 MiB (Content-Range: bytes inicio-fin/total y X-Sunfish-Upload: <id>). Los trozos deben llegar en orden; el temporal es .sunfish-<id>.part y el último trozo lo renombra a su sitio (202 por trozo, 201/204 al terminar). Un PUT sin Content-Range sube el fichero entero.
  • Respeta Upload y Overwrite; comprueba el espacio libre si el tamaño se conoce de antemano.
  • Pendiente: limpiar .sunfish-*.part abandonados por subidas que no terminaron.

Formularios y parámetros

Petición Qué hace
POST carpeta/ multipart, a=upload Subir ficheros (campo f, varios a la vez)
POST carpeta/ formulario, a=mkdir&name= Crear carpeta
POST carpeta/ formulario, a=rename&name=&to= Renombrar dentro de la carpeta
POST carpeta/ formulario, a=delete&name= Borrar fichero o carpeta entera
GET carpeta/?$rename=nombre Página para escribir el nombre nuevo
GET carpeta/?$delete=nombre Página de confirmación del borrado
GET carpeta/?$zip Carpeta en ZIP
  • Todo POST lleva t, el token anti-CSRF de esa acción y carpeta (ver SEGURIDAD.md). En multipart, a y t van antes que los ficheros y se validan antes de escribir nada.
  • Tras un POST correcto se vuelve a la carpeta con 303 (HTTP/1.1) o 302 (HTTP/1.0, que no conoce el 303).
  • Los nombres nuevos no pueden empezar por . (quedarían ocultos en el listado).

Usuarios

Un share con Users exige HTTP Basic. Las cuentas se gestionan con sunfish-cli user add|passwd|del|list (contraseña pedida sin eco). Ver SEGURIDAD.md.

Descargar carpetas en ZIP

  • Se genera en flujo, sin fichero temporal (la 2.0 creaba un ZIP temporal completo).
  • Compatible con descompresores antiguos (PKZIP 2.x, StuffIt):
    • Método Stored con el CRC calculado antes de cada fichero. No necesita data descriptors y permite enviar Content-Length, a costa de leer dos veces cada fichero.
    • Nombres no ASCII: en CP437 para clientes antiguos (si se pueden representar) y en UTF-8 con su indicador para los modernos, que leen como UTF-8 los nombres sin indicador.
    • Sin ocultos ni enlaces; las locations hijas (otros shares) no se incluyen.
    • Pendiente: ZIP64 (hoy, más de 4 GiB o 65535 entradas da un error) y Deflate opcional.

Autodescarga del servidor

/$sunfish/download entrega el propio ejecutable que está sirviendo (como hacía la 2.0), para repartirlo por la LAN. Se desactiva con SelfDownload = false.

Panel de administración

En /$sunfish/admin/, desactivado por defecto.

Implementado en Web/WebAdmin.cs (fase 7). Se activa desde la GUI (ventana de información), con sunfish-cli admin enable o con [Admin] Enabled = true y una contraseña.

  • Acceso: usuario admin con contraseña (HTTP Basic); hash PBKDF2 en [Admin]. Mismo bloqueo por intentos fallidos que el resto. Desactivado o sin contraseña responde 404.
  • AllowFrom: IP sueltas, redes CIDR (192.168.1.0/24, fd00::/8) o *. Por defecto, 127.0.0.1, ::1. Desde otra dirección responde 403. Las IPv4 que llegan como IPv6 (::ffff:a.b.c.d) se comparan como IPv4.
  • Páginas:
    • /$sunfish/admin/: estado (HTTP y FTP), direcciones con su enlace [QR], servidor (nombre, menú raíz, FTP sí/no y puerto), shares (editar, quitar), usuarios (alta, contraseña, baja) y ajustes del propio panel;
    • /$sunfish/admin/share y share?n=NOMBRE: alta y edición de un share con todas sus opciones (las mismas validaciones que la GUI);
    • /$sunfish/admin/log: el registro reciente;
    • /$sunfish/admin/qr?u=URL: PNG con el QR (solo http:// y ftp://, hasta 300 caracteres).
  • El HTTP en sí no se cambia desde el panel (puerto, interfaces, activarlo): reiniciar el servidor HTTP cortaría la propia conexión del panel. Se cambia desde la aplicación.
  • HTML clásico también, para poder administrar desde cualquier navegador; sin JavaScript.
  • Formularios POST con token anti-CSRF (ver SEGURIDAD.md). Tras guardar, redirección 303 (302 en HTTP/1.0) a ?ok; si hay un error, la misma página con el aviso.
  • Los cambios se guardan en el fichero de configuración al momento (o solo se aplican en el modo rápido, que no tiene fichero).
  • Borrar un usuario lo quita también de los shares; admin no vale como nombre de usuario.
  • Aviso visible: la contraseña viaja en claro por la red.
  • Pendiente: lista de conexiones activas.