# HG changeset patch # User ellis # Date 1684201224 14400 # Node ID c520966de7faa92d4b35af8c8e65c17cbb714d58 # Parent d8f806f1d32714bab589b1bcabd62bf2773b897a added quiche.h and some ui work diff -r d8f806f1d327 -r c520966de7fa demo.asd --- a/demo.asd Sun May 14 21:27:04 2023 -0400 +++ b/demo.asd Mon May 15 21:40:24 2023 -0400 @@ -10,8 +10,8 @@ :depends-on (:bordeaux-threads #+(or ccl sbcl) :clack - :caveman2 :clog + :clog-web :cl-rocksdb :verbose :alexandria diff -r d8f806f1d327 -r c520966de7fa ffi/quiche.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ffi/quiche.h Mon May 15 21:40:24 2023 -0400 @@ -0,0 +1,973 @@ +// Copyright (C) 2018-2019, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QUICHE_H +#define QUICHE_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include +#include +#include + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) +#include +#include +#include +#else +#include +#include +#endif + +#ifdef __unix__ +#include +#endif +#ifdef _MSC_VER +#include +#define ssize_t SSIZE_T +#endif + +// QUIC transport API. +// + +// The current QUIC wire version. +#define QUICHE_PROTOCOL_VERSION 0x00000001 + +// The maximum length of a connection ID. +#define QUICHE_MAX_CONN_ID_LEN 20 + +// The minimum length of Initial packets sent by a client. +#define QUICHE_MIN_CLIENT_INITIAL_LEN 1200 + +enum quiche_error { + // There is no more work to do. + QUICHE_ERR_DONE = -1, + + // The provided buffer is too short. + QUICHE_ERR_BUFFER_TOO_SHORT = -2, + + // The provided packet cannot be parsed because its version is unknown. + QUICHE_ERR_UNKNOWN_VERSION = -3, + + // The provided packet cannot be parsed because it contains an invalid + // frame. + QUICHE_ERR_INVALID_FRAME = -4, + + // The provided packet cannot be parsed. + QUICHE_ERR_INVALID_PACKET = -5, + + // The operation cannot be completed because the connection is in an + // invalid state. + QUICHE_ERR_INVALID_STATE = -6, + + // The operation cannot be completed because the stream is in an + // invalid state. + QUICHE_ERR_INVALID_STREAM_STATE = -7, + + // The peer's transport params cannot be parsed. + QUICHE_ERR_INVALID_TRANSPORT_PARAM = -8, + + // A cryptographic operation failed. + QUICHE_ERR_CRYPTO_FAIL = -9, + + // The TLS handshake failed. + QUICHE_ERR_TLS_FAIL = -10, + + // The peer violated the local flow control limits. + QUICHE_ERR_FLOW_CONTROL = -11, + + // The peer violated the local stream limits. + QUICHE_ERR_STREAM_LIMIT = -12, + + // The specified stream was stopped by the peer. + QUICHE_ERR_STREAM_STOPPED = -15, + + // The specified stream was reset by the peer. + QUICHE_ERR_STREAM_RESET = -16, + + // The received data exceeds the stream's final size. + QUICHE_ERR_FINAL_SIZE = -13, + + // Error in congestion control. + QUICHE_ERR_CONGESTION_CONTROL = -14, + + // Too many identifiers were provided. + QUICHE_ERR_ID_LIMIT = -17, + + // Not enough available identifiers. + QUICHE_ERR_OUT_OF_IDENTIFIERS = -18, + + // Error in key update. + QUICHE_ERR_KEY_UPDATE = -19, +}; + +// Returns a human readable string with the quiche version number. +const char *quiche_version(void); + +// Enables logging. |cb| will be called with log messages +int quiche_enable_debug_logging(void (*cb)(const char *line, void *argp), + void *argp); + +// Stores configuration shared between multiple connections. +typedef struct quiche_config quiche_config; + +// Creates a config object with the given version. +quiche_config *quiche_config_new(uint32_t version); + +// Configures the given certificate chain. +int quiche_config_load_cert_chain_from_pem_file(quiche_config *config, + const char *path); + +// Configures the given private key. +int quiche_config_load_priv_key_from_pem_file(quiche_config *config, + const char *path); + +// Specifies a file where trusted CA certificates are stored for the purposes of certificate verification. +int quiche_config_load_verify_locations_from_file(quiche_config *config, + const char *path); + +// Specifies a directory where trusted CA certificates are stored for the purposes of certificate verification. +int quiche_config_load_verify_locations_from_directory(quiche_config *config, + const char *path); + +// Configures whether to verify the peer's certificate. +void quiche_config_verify_peer(quiche_config *config, bool v); + +// Configures whether to send GREASE. +void quiche_config_grease(quiche_config *config, bool v); + +// Enables logging of secrets. +void quiche_config_log_keys(quiche_config *config); + +// Enables sending or receiving early data. +void quiche_config_enable_early_data(quiche_config *config); + +// Configures the list of supported application protocols. +int quiche_config_set_application_protos(quiche_config *config, + const uint8_t *protos, + size_t protos_len); + +// Sets the `max_idle_timeout` transport parameter, in milliseconds, default is +// no timeout. +void quiche_config_set_max_idle_timeout(quiche_config *config, uint64_t v); + +// Sets the `max_udp_payload_size transport` parameter. +void quiche_config_set_max_recv_udp_payload_size(quiche_config *config, size_t v); + +// Sets the maximum outgoing UDP payload size. +void quiche_config_set_max_send_udp_payload_size(quiche_config *config, size_t v); + +// Sets the `initial_max_data` transport parameter. +void quiche_config_set_initial_max_data(quiche_config *config, uint64_t v); + +// Sets the `initial_max_stream_data_bidi_local` transport parameter. +void quiche_config_set_initial_max_stream_data_bidi_local(quiche_config *config, uint64_t v); + +// Sets the `initial_max_stream_data_bidi_remote` transport parameter. +void quiche_config_set_initial_max_stream_data_bidi_remote(quiche_config *config, uint64_t v); + +// Sets the `initial_max_stream_data_uni` transport parameter. +void quiche_config_set_initial_max_stream_data_uni(quiche_config *config, uint64_t v); + +// Sets the `initial_max_streams_bidi` transport parameter. +void quiche_config_set_initial_max_streams_bidi(quiche_config *config, uint64_t v); + +// Sets the `initial_max_streams_uni` transport parameter. +void quiche_config_set_initial_max_streams_uni(quiche_config *config, uint64_t v); + +// Sets the `ack_delay_exponent` transport parameter. +void quiche_config_set_ack_delay_exponent(quiche_config *config, uint64_t v); + +// Sets the `max_ack_delay` transport parameter. +void quiche_config_set_max_ack_delay(quiche_config *config, uint64_t v); + +// Sets the `disable_active_migration` transport parameter. +void quiche_config_set_disable_active_migration(quiche_config *config, bool v); + +enum quiche_cc_algorithm { + QUICHE_CC_RENO = 0, + QUICHE_CC_CUBIC = 1, + QUICHE_CC_BBR = 2, +}; + +// Sets the congestion control algorithm used. +void quiche_config_set_cc_algorithm(quiche_config *config, enum quiche_cc_algorithm algo); + +// Configures whether to use HyStart++. +void quiche_config_enable_hystart(quiche_config *config, bool v); + +// Configures whether to enable pacing (enabled by default). +void quiche_config_enable_pacing(quiche_config *config, bool v); + +// Configures max pacing rate to be used. +void quiche_config_set_max_pacing_rate(quiche_config *config, uint64_t v); + +// Configures whether to enable receiving DATAGRAM frames. +void quiche_config_enable_dgram(quiche_config *config, bool enabled, + size_t recv_queue_len, + size_t send_queue_len); + +// Sets the maximum connection window. +void quiche_config_set_max_connection_window(quiche_config *config, uint64_t v); + +// Sets the maximum stream window. +void quiche_config_set_max_stream_window(quiche_config *config, uint64_t v); + +// Sets the limit of active connection IDs. +void quiche_config_set_active_connection_id_limit(quiche_config *config, uint64_t v); + +// Sets the initial stateless reset token. |v| must contain 16 bytes, otherwise the behaviour is undefined. +void quiche_config_set_stateless_reset_token(quiche_config *config, const uint8_t *v); + +// Frees the config object. +void quiche_config_free(quiche_config *config); + +// Extracts version, type, source / destination connection ID and address +// verification token from the packet in |buf|. +int quiche_header_info(const uint8_t *buf, size_t buf_len, size_t dcil, + uint32_t *version, uint8_t *type, + uint8_t *scid, size_t *scid_len, + uint8_t *dcid, size_t *dcid_len, + uint8_t *token, size_t *token_len); + +// A QUIC connection. +typedef struct quiche_conn quiche_conn; + +// Creates a new server-side connection. +quiche_conn *quiche_accept(const uint8_t *scid, size_t scid_len, + const uint8_t *odcid, size_t odcid_len, + const struct sockaddr *local, size_t local_len, + const struct sockaddr *peer, size_t peer_len, + quiche_config *config); + +// Creates a new client-side connection. +quiche_conn *quiche_connect(const char *server_name, + const uint8_t *scid, size_t scid_len, + const struct sockaddr *local, size_t local_len, + const struct sockaddr *peer, size_t peer_len, + quiche_config *config); + +// Writes a version negotiation packet. +ssize_t quiche_negotiate_version(const uint8_t *scid, size_t scid_len, + const uint8_t *dcid, size_t dcid_len, + uint8_t *out, size_t out_len); + +// Writes a retry packet. +ssize_t quiche_retry(const uint8_t *scid, size_t scid_len, + const uint8_t *dcid, size_t dcid_len, + const uint8_t *new_scid, size_t new_scid_len, + const uint8_t *token, size_t token_len, + uint32_t version, uint8_t *out, size_t out_len); + +// Returns true if the given protocol version is supported. +bool quiche_version_is_supported(uint32_t version); + +quiche_conn *quiche_conn_new_with_tls(const uint8_t *scid, size_t scid_len, + const uint8_t *odcid, size_t odcid_len, + const struct sockaddr *local, size_t local_len, + const struct sockaddr *peer, size_t peer_len, + quiche_config *config, void *ssl, + bool is_server); + +// Enables keylog to the specified file path. Returns true on success. +bool quiche_conn_set_keylog_path(quiche_conn *conn, const char *path); + +// Enables keylog to the specified file descriptor. Unix only. +void quiche_conn_set_keylog_fd(quiche_conn *conn, int fd); + +// Enables qlog to the specified file path. Returns true on success. +bool quiche_conn_set_qlog_path(quiche_conn *conn, const char *path, + const char *log_title, const char *log_desc); + +// Enables qlog to the specified file descriptor. Unix only. +void quiche_conn_set_qlog_fd(quiche_conn *conn, int fd, const char *log_title, + const char *log_desc); + +// Configures the given session for resumption. +int quiche_conn_set_session(quiche_conn *conn, const uint8_t *buf, size_t buf_len); + +typedef struct { + // The remote address the packet was received from. + struct sockaddr *from; + socklen_t from_len; + + // The local address the packet was received on. + struct sockaddr *to; + socklen_t to_len; +} quiche_recv_info; + +// Processes QUIC packets received from the peer. +ssize_t quiche_conn_recv(quiche_conn *conn, uint8_t *buf, size_t buf_len, + const quiche_recv_info *info); + +typedef struct { + // The local address the packet should be sent from. + struct sockaddr_storage from; + socklen_t from_len; + + // The remote address the packet should be sent to. + struct sockaddr_storage to; + socklen_t to_len; + + // The time to send the packet out. + struct timespec at; +} quiche_send_info; + +// Writes a single QUIC packet to be sent to the peer. +ssize_t quiche_conn_send(quiche_conn *conn, uint8_t *out, size_t out_len, + quiche_send_info *out_info); + +// Returns the size of the send quantum, in bytes. +size_t quiche_conn_send_quantum(const quiche_conn *conn); + +// Reads contiguous data from a stream. +ssize_t quiche_conn_stream_recv(quiche_conn *conn, uint64_t stream_id, + uint8_t *out, size_t buf_len, bool *fin); + +// Writes data to a stream. +ssize_t quiche_conn_stream_send(quiche_conn *conn, uint64_t stream_id, + const uint8_t *buf, size_t buf_len, bool fin); + +// The side of the stream to be shut down. +enum quiche_shutdown { + QUICHE_SHUTDOWN_READ = 0, + QUICHE_SHUTDOWN_WRITE = 1, +}; + +// Sets the priority for a stream. +int quiche_conn_stream_priority(quiche_conn *conn, uint64_t stream_id, + uint8_t urgency, bool incremental); + +// Shuts down reading or writing from/to the specified stream. +int quiche_conn_stream_shutdown(quiche_conn *conn, uint64_t stream_id, + enum quiche_shutdown direction, uint64_t err); + +// Returns the stream's send capacity in bytes. +ssize_t quiche_conn_stream_capacity(const quiche_conn *conn, uint64_t stream_id); + +// Returns true if the stream has data that can be read. +bool quiche_conn_stream_readable(const quiche_conn *conn, uint64_t stream_id); + +// Returns the next stream that has data to read, or -1 if no such stream is +// available. +int64_t quiche_conn_stream_readable_next(quiche_conn *conn); + +// Returns true if the stream has enough send capacity. +// +// On error a value lower than 0 is returned. +int quiche_conn_stream_writable(quiche_conn *conn, uint64_t stream_id, size_t len); + +// Returns the next stream that can be written to, or -1 if no such stream is +// available. +int64_t quiche_conn_stream_writable_next(quiche_conn *conn); + +// Returns true if all the data has been read from the specified stream. +bool quiche_conn_stream_finished(const quiche_conn *conn, uint64_t stream_id); + +typedef struct quiche_stream_iter quiche_stream_iter; + +// Returns an iterator over streams that have outstanding data to read. +quiche_stream_iter *quiche_conn_readable(const quiche_conn *conn); + +// Returns an iterator over streams that can be written to. +quiche_stream_iter *quiche_conn_writable(const quiche_conn *conn); + +// Returns the maximum possible size of egress UDP payloads. +size_t quiche_conn_max_send_udp_payload_size(const quiche_conn *conn); + +// Returns the amount of time until the next timeout event, in nanoseconds. +uint64_t quiche_conn_timeout_as_nanos(const quiche_conn *conn); + +// Returns the amount of time until the next timeout event, in milliseconds. +uint64_t quiche_conn_timeout_as_millis(const quiche_conn *conn); + +// Processes a timeout event. +void quiche_conn_on_timeout(quiche_conn *conn); + +// Closes the connection with the given error and reason. +int quiche_conn_close(quiche_conn *conn, bool app, uint64_t err, + const uint8_t *reason, size_t reason_len); + +// Returns a string uniquely representing the connection. +void quiche_conn_trace_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len); + +// Returns the source connection ID. +void quiche_conn_source_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len); + +// Returns the destination connection ID. +void quiche_conn_destination_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len); + +// Returns the negotiated ALPN protocol. +void quiche_conn_application_proto(const quiche_conn *conn, const uint8_t **out, + size_t *out_len); + +// Returns the peer's leaf certificate (if any) as a DER-encoded buffer. +void quiche_conn_peer_cert(const quiche_conn *conn, const uint8_t **out, size_t *out_len); + +// Returns the serialized cryptographic session for the connection. +void quiche_conn_session(const quiche_conn *conn, const uint8_t **out, size_t *out_len); + +// Returns true if the connection handshake is complete. +bool quiche_conn_is_established(const quiche_conn *conn); + +// Returns true if the connection has a pending handshake that has progressed +// enough to send or receive early data. +bool quiche_conn_is_in_early_data(const quiche_conn *conn); + +// Returns whether there is stream or DATAGRAM data available to read. +bool quiche_conn_is_readable(const quiche_conn *conn); + +// Returns true if the connection is draining. +bool quiche_conn_is_draining(const quiche_conn *conn); + +// Returns the number of bidirectional streams that can be created +// before the peer's stream count limit is reached. +uint64_t quiche_conn_peer_streams_left_bidi(const quiche_conn *conn); + +// Returns the number of unidirectional streams that can be created +// before the peer's stream count limit is reached. +uint64_t quiche_conn_peer_streams_left_uni(const quiche_conn *conn); + +// Returns true if the connection is closed. +bool quiche_conn_is_closed(const quiche_conn *conn); + +// Returns true if the connection was closed due to the idle timeout. +bool quiche_conn_is_timed_out(const quiche_conn *conn); + +// Returns true if a connection error was received, and updates the provided +// parameters accordingly. +bool quiche_conn_peer_error(const quiche_conn *conn, + bool *is_app, + uint64_t *error_code, + const uint8_t **reason, + size_t *reason_len); + +// Returns true if a connection error was queued or sent, and updates the provided +// parameters accordingly. +bool quiche_conn_local_error(const quiche_conn *conn, + bool *is_app, + uint64_t *error_code, + const uint8_t **reason, + size_t *reason_len); + +// Initializes the stream's application data. +// +// Stream data can only be initialized once. Additional calls to this method +// will fail. +// +// Note that the application is responsible for freeing the data. +int quiche_conn_stream_init_application_data(quiche_conn *conn, + uint64_t stream_id, + void *data); + +// Returns the stream's application data, if any was initialized. +void *quiche_conn_stream_application_data(quiche_conn *conn, uint64_t stream_id); + +// Fetches the next stream from the given iterator. Returns false if there are +// no more elements in the iterator. +bool quiche_stream_iter_next(quiche_stream_iter *iter, uint64_t *stream_id); + +// Frees the given stream iterator object. +void quiche_stream_iter_free(quiche_stream_iter *iter); + +typedef struct { + // The number of QUIC packets received on this connection. + size_t recv; + + // The number of QUIC packets sent on this connection. + size_t sent; + + // The number of QUIC packets that were lost. + size_t lost; + + // The number of sent QUIC packets with retransmitted data. + size_t retrans; + + // The number of sent bytes. + uint64_t sent_bytes; + + // The number of received bytes. + uint64_t recv_bytes; + + // The number of bytes lost. + uint64_t lost_bytes; + + // The number of stream bytes retransmitted. + uint64_t stream_retrans_bytes; + + // The number of known paths for the connection. + size_t paths_count; + + // The maximum idle timeout. + uint64_t peer_max_idle_timeout; + + // The maximum UDP payload size. + uint64_t peer_max_udp_payload_size; + + // The initial flow control maximum data for the connection. + uint64_t peer_initial_max_data; + + // The initial flow control maximum data for local bidirectional streams. + uint64_t peer_initial_max_stream_data_bidi_local; + + // The initial flow control maximum data for remote bidirectional streams. + uint64_t peer_initial_max_stream_data_bidi_remote; + + // The initial flow control maximum data for unidirectional streams. + uint64_t peer_initial_max_stream_data_uni; + + // The initial maximum bidirectional streams. + uint64_t peer_initial_max_streams_bidi; + + // The initial maximum unidirectional streams. + uint64_t peer_initial_max_streams_uni; + + // The ACK delay exponent. + uint64_t peer_ack_delay_exponent; + + // The max ACK delay. + uint64_t peer_max_ack_delay; + + // Whether active migration is disabled. + bool peer_disable_active_migration; + + // The active connection ID limit. + uint64_t peer_active_conn_id_limit; + + // DATAGRAM frame extension parameter, if any. + ssize_t peer_max_datagram_frame_size; +} quiche_stats; + +// Collects and returns statistics about the connection. +void quiche_conn_stats(const quiche_conn *conn, quiche_stats *out); + +typedef struct { + // The local address used by this path. + struct sockaddr_storage local_addr; + socklen_t local_addr_len; + + // The peer address seen by this path. + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; + + // The validation state of the path. + ssize_t validation_state; + + // Whether this path is active. + bool active; + + // The number of QUIC packets received on this path. + size_t recv; + + // The number of QUIC packets sent on this path. + size_t sent; + + // The number of QUIC packets that were lost on this path. + size_t lost; + + // The number of sent QUIC packets with retransmitted data on this path. + size_t retrans; + + // The estimated round-trip time of the path (in nanoseconds). + uint64_t rtt; + + // The size of the path's congestion window in bytes. + size_t cwnd; + + // The number of sent bytes on this path. + uint64_t sent_bytes; + + // The number of received bytes on this path. + uint64_t recv_bytes; + + // The number of bytes lost on this path. + uint64_t lost_bytes; + + // The number of stream bytes retransmitted on this path. + uint64_t stream_retrans_bytes; + + // The current PMTU for the path. + size_t pmtu; + + // The most recent data delivery rate estimate in bytes/s. + uint64_t delivery_rate; +} quiche_path_stats; + + +// Collects and returns statistics about the specified path for the connection. +// +// The `idx` argument represent the path's index (also see the `paths_count` +// field of `quiche_stats`). +int quiche_conn_path_stats(const quiche_conn *conn, size_t idx, quiche_path_stats *out); + +// Returns whether or not this is a server-side connection. +bool quiche_conn_is_server(const quiche_conn *conn); + +// Returns the maximum DATAGRAM payload that can be sent. +ssize_t quiche_conn_dgram_max_writable_len(const quiche_conn *conn); + +// Returns the length of the first stored DATAGRAM. +ssize_t quiche_conn_dgram_recv_front_len(const quiche_conn *conn); + +// Returns the number of items in the DATAGRAM receive queue. +ssize_t quiche_conn_dgram_recv_queue_len(const quiche_conn *conn); + +// Returns the total size of all items in the DATAGRAM receive queue. +ssize_t quiche_conn_dgram_recv_queue_byte_size(const quiche_conn *conn); + +// Returns the number of items in the DATAGRAM send queue. +ssize_t quiche_conn_dgram_send_queue_len(const quiche_conn *conn); + +// Returns the total size of all items in the DATAGRAM send queue. +ssize_t quiche_conn_dgram_send_queue_byte_size(const quiche_conn *conn); + +// Reads the first received DATAGRAM. +ssize_t quiche_conn_dgram_recv(quiche_conn *conn, uint8_t *buf, + size_t buf_len); + +// Sends data in a DATAGRAM frame. +ssize_t quiche_conn_dgram_send(quiche_conn *conn, const uint8_t *buf, + size_t buf_len); + +// Purges queued outgoing DATAGRAMs matching the predicate. +void quiche_conn_dgram_purge_outgoing(quiche_conn *conn, + bool (*f)(uint8_t *, size_t)); + +// Schedule an ack-eliciting packet on the active path. +ssize_t quiche_conn_send_ack_eliciting(quiche_conn *conn); + +// Schedule an ack-eliciting packet on the specified path. +ssize_t quiche_conn_send_ack_eliciting_on_path(quiche_conn *conn, + const struct sockaddr *local, size_t local_len, + const struct sockaddr *peer, size_t peer_len); + +// Frees the connection object. +void quiche_conn_free(quiche_conn *conn); + + +// HTTP/3 API +// + +// List of ALPN tokens of supported HTTP/3 versions. +#define QUICHE_H3_APPLICATION_PROTOCOL "\x02h3\x05h3-29\x05h3-28\x05h3-27" + +enum quiche_h3_error { + // There is no error or no work to do + QUICHE_H3_ERR_DONE = -1, + + // The provided buffer is too short. + QUICHE_H3_ERR_BUFFER_TOO_SHORT = -2, + + // Internal error in the HTTP/3 stack. + QUICHE_H3_ERR_INTERNAL_ERROR = -3, + + // Endpoint detected that the peer is exhibiting behavior that causes. + // excessive load. + QUICHE_H3_ERR_EXCESSIVE_LOAD = -4, + + // Stream ID or Push ID greater that current maximum was + // used incorrectly, such as exceeding a limit, reducing a limit, + // or being reused. + QUICHE_H3_ERR_ID_ERROR= -5, + + // The endpoint detected that its peer created a stream that it will not + // accept. + QUICHE_H3_ERR_STREAM_CREATION_ERROR = -6, + + // A required critical stream was closed. + QUICHE_H3_ERR_CLOSED_CRITICAL_STREAM = -7, + + // No SETTINGS frame at beginning of control stream. + QUICHE_H3_ERR_MISSING_SETTINGS = -8, + + // A frame was received which is not permitted in the current state. + QUICHE_H3_ERR_FRAME_UNEXPECTED = -9, + + // Frame violated layout or size rules. + QUICHE_H3_ERR_FRAME_ERROR = -10, + + // QPACK Header block decompression failure. + QUICHE_H3_ERR_QPACK_DECOMPRESSION_FAILED = -11, + + // -12 was previously used for TransportError, skip it + + // The underlying QUIC stream (or connection) doesn't have enough capacity + // for the operation to complete. The application should retry later on. + QUICHE_H3_ERR_STREAM_BLOCKED = -13, + + // Error in the payload of a SETTINGS frame. + QUICHE_H3_ERR_SETTINGS_ERROR = -14, + + // Server rejected request. + QUICHE_H3_ERR_REQUEST_REJECTED = -15, + + // Request or its response cancelled. + QUICHE_H3_ERR_REQUEST_CANCELLED = -16, + + // Client's request stream terminated without containing a full-formed + // request. + QUICHE_H3_ERR_REQUEST_INCOMPLETE = -17, + + // An HTTP message was malformed and cannot be processed. + QUICHE_H3_ERR_MESSAGE_ERROR = -18, + + // The TCP connection established in response to a CONNECT request was + // reset or abnormally closed. + QUICHE_H3_ERR_CONNECT_ERROR = -19, + + // The requested operation cannot be served over HTTP/3. Peer should retry + // over HTTP/1.1. + QUICHE_H3_ERR_VERSION_FALLBACK = -20, + + // The following QUICHE_H3_TRANSPORT_ERR_* errors are propagated + // from the QUIC transport layer. + + // See QUICHE_ERR_DONE. + QUICHE_H3_TRANSPORT_ERR_DONE = QUICHE_ERR_DONE - 1000, + + // See QUICHE_ERR_BUFFER_TOO_SHORT. + QUICHE_H3_TRANSPORT_ERR_BUFFER_TOO_SHORT = QUICHE_ERR_BUFFER_TOO_SHORT - 1000, + + // See QUICHE_ERR_UNKNOWN_VERSION. + QUICHE_H3_TRANSPORT_ERR_UNKNOWN_VERSION = QUICHE_ERR_UNKNOWN_VERSION - 1000, + + // See QUICHE_ERR_INVALID_FRAME. + QUICHE_H3_TRANSPORT_ERR_INVALID_FRAME = QUICHE_ERR_INVALID_FRAME - 1000, + + // See QUICHE_ERR_INVALID_PACKET. + QUICHE_H3_TRANSPORT_ERR_INVALID_PACKET = QUICHE_ERR_INVALID_PACKET - 1000, + + // See QUICHE_ERR_INVALID_STATE. + QUICHE_H3_TRANSPORT_ERR_INVALID_STATE = QUICHE_ERR_INVALID_STATE - 1000, + + // See QUICHE_ERR_INVALID_STREAM_STATE. + QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE = QUICHE_ERR_INVALID_STREAM_STATE - 1000, + + // See QUICHE_ERR_INVALID_TRANSPORT_PARAM. + QUICHE_H3_TRANSPORT_ERR_INVALID_TRANSPORT_PARAM = QUICHE_ERR_INVALID_TRANSPORT_PARAM - 1000, + + // See QUICHE_ERR_CRYPTO_FAIL. + QUICHE_H3_TRANSPORT_ERR_CRYPTO_FAIL = QUICHE_ERR_CRYPTO_FAIL - 1000, + + // See QUICHE_ERR_TLS_FAIL. + QUICHE_H3_TRANSPORT_ERR_TLS_FAIL = QUICHE_ERR_TLS_FAIL - 1000, + + // See QUICHE_ERR_FLOW_CONTROL. + QUICHE_H3_TRANSPORT_ERR_FLOW_CONTROL = QUICHE_ERR_FLOW_CONTROL - 1000, + + // See QUICHE_ERR_STREAM_LIMIT. + QUICHE_H3_TRANSPORT_ERR_STREAM_LIMIT = QUICHE_ERR_STREAM_LIMIT - 1000, + + // See QUICHE_ERR_STREAM_STOPPED. + QUICHE_H3_TRANSPORT_ERR_STREAM_STOPPED = QUICHE_ERR_STREAM_STOPPED - 1000, + + // See QUICHE_ERR_STREAM_RESET. + QUICHE_H3_TRANSPORT_ERR_STREAM_RESET = QUICHE_ERR_STREAM_RESET - 1000, + + // See QUICHE_ERR_FINAL_SIZE. + QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE = QUICHE_ERR_FINAL_SIZE - 1000, + + // See QUICHE_ERR_CONGESTION_CONTROL. + QUICHE_H3_TRANSPORT_ERR_CONGESTION_CONTROL = QUICHE_ERR_CONGESTION_CONTROL - 1000, + + // See QUICHE_ERR_ID_LIMIT. + QUICHE_H3_TRANSPORT_ERR_ID_LIMIT = QUICHE_ERR_ID_LIMIT - 1000, + + // See QUICHE_ERR_OUT_OF_IDENTIFIERS. + QUICHE_H3_TRANSPORT_ERR_OUT_OF_IDENTIFIERS = QUICHE_ERR_OUT_OF_IDENTIFIERS - 1000, + + // See QUICHE_ERR_KEY_UPDATE. + QUICHE_H3_TRANSPORT_ERR_KEY_UPDATE = QUICHE_ERR_KEY_UPDATE - 1000, +}; + +// Stores configuration shared between multiple connections. +typedef struct quiche_h3_config quiche_h3_config; + +// Creates an HTTP/3 config object with default settings values. +quiche_h3_config *quiche_h3_config_new(void); + +// Sets the `SETTINGS_MAX_FIELD_SECTION_SIZE` setting. +void quiche_h3_config_set_max_field_section_size(quiche_h3_config *config, uint64_t v); + +// Sets the `SETTINGS_QPACK_MAX_TABLE_CAPACITY` setting. +void quiche_h3_config_set_qpack_max_table_capacity(quiche_h3_config *config, uint64_t v); + +// Sets the `SETTINGS_QPACK_BLOCKED_STREAMS` setting. +void quiche_h3_config_set_qpack_blocked_streams(quiche_h3_config *config, uint64_t v); + +// Sets the `SETTINGS_ENABLE_CONNECT_PROTOCOL` setting. +void quiche_h3_config_enable_extended_connect(quiche_h3_config *config, bool enabled); + +// Frees the HTTP/3 config object. +void quiche_h3_config_free(quiche_h3_config *config); + +// An HTTP/3 connection. +typedef struct quiche_h3_conn quiche_h3_conn; + +// Creates a new server-side connection. +quiche_h3_conn *quiche_h3_accept(quiche_conn *quiche_conn, + quiche_h3_config *config); + +// Creates a new HTTP/3 connection using the provided QUIC connection. +quiche_h3_conn *quiche_h3_conn_new_with_transport(quiche_conn *quiche_conn, + quiche_h3_config *config); + +enum quiche_h3_event_type { + QUICHE_H3_EVENT_HEADERS, + QUICHE_H3_EVENT_DATA, + QUICHE_H3_EVENT_FINISHED, + QUICHE_H3_EVENT_DATAGRAM, + QUICHE_H3_EVENT_GOAWAY, + QUICHE_H3_EVENT_RESET, + QUICHE_H3_EVENT_PRIORITY_UPDATE, +}; + +typedef struct quiche_h3_event quiche_h3_event; + +// Processes HTTP/3 data received from the peer. +int64_t quiche_h3_conn_poll(quiche_h3_conn *conn, quiche_conn *quic_conn, + quiche_h3_event **ev); + +// Returns the type of the event. +enum quiche_h3_event_type quiche_h3_event_type(quiche_h3_event *ev); + +// Iterates over the headers in the event. +// +// The `cb` callback will be called for each header in `ev`. `cb` should check +// the validity of pseudo-headers and headers. If `cb` returns any value other +// than `0`, processing will be interrupted and the value is returned to the +// caller. +int quiche_h3_event_for_each_header(quiche_h3_event *ev, + int (*cb)(uint8_t *name, size_t name_len, + uint8_t *value, size_t value_len, + void *argp), + void *argp); + +// Iterates over the peer's HTTP/3 settings. +// +// The `cb` callback will be called for each setting in `conn`. +// If `cb` returns any value other than `0`, processing will be interrupted and +// the value is returned to the caller. +int quiche_h3_for_each_setting(quiche_h3_conn *conn, + int (*cb)(uint64_t identifier, + uint64_t value, void *argp), + void *argp); + +// Check whether data will follow the headers on the stream. +bool quiche_h3_event_headers_has_body(quiche_h3_event *ev); + +// Check whether or not extended connection is enabled by the peer +bool quiche_h3_extended_connect_enabled_by_peer(quiche_h3_conn *conn); + +// Frees the HTTP/3 event object. +void quiche_h3_event_free(quiche_h3_event *ev); + +typedef struct { + const uint8_t *name; + size_t name_len; + + const uint8_t *value; + size_t value_len; +} quiche_h3_header; + +// Extensible Priorities parameters. +typedef struct { + uint8_t urgency; + bool incremental; +} quiche_h3_priority; + +// Sends an HTTP/3 request. +int64_t quiche_h3_send_request(quiche_h3_conn *conn, quiche_conn *quic_conn, + quiche_h3_header *headers, size_t headers_len, + bool fin); + +// Sends an HTTP/3 response on the specified stream with default priority. +int quiche_h3_send_response(quiche_h3_conn *conn, quiche_conn *quic_conn, + uint64_t stream_id, quiche_h3_header *headers, + size_t headers_len, bool fin); + +// Sends an HTTP/3 response on the specified stream with specified priority. +int quiche_h3_send_response_with_priority(quiche_h3_conn *conn, + quiche_conn *quic_conn, uint64_t stream_id, + quiche_h3_header *headers, size_t headers_len, + quiche_h3_priority *priority, bool fin); + +// Sends an HTTP/3 body chunk on the given stream. +ssize_t quiche_h3_send_body(quiche_h3_conn *conn, quiche_conn *quic_conn, + uint64_t stream_id, uint8_t *body, size_t body_len, + bool fin); + +// Reads request or response body data into the provided buffer. +ssize_t quiche_h3_recv_body(quiche_h3_conn *conn, quiche_conn *quic_conn, + uint64_t stream_id, uint8_t *out, size_t out_len); + +// Try to parse an Extensible Priority field value. +int quiche_h3_parse_extensible_priority(uint8_t *priority, + size_t priority_len, + quiche_h3_priority *parsed); + +/// Sends a PRIORITY_UPDATE frame on the control stream with specified +/// request stream ID and priority. +int quiche_h3_send_priority_update_for_request(quiche_h3_conn *conn, + quiche_conn *quic_conn, + uint64_t stream_id, + quiche_h3_priority *priority); + +// Take the last received PRIORITY_UPDATE frame for a stream. +// +// The `cb` callback will be called once. `cb` should check the validity of +// priority field value contents. If `cb` returns any value other than `0`, +// processing will be interrupted and the value is returned to the caller. +int quiche_h3_take_last_priority_update(quiche_h3_conn *conn, + uint64_t prioritized_element_id, + int (*cb)(uint8_t *priority_field_value, + uint64_t priority_field_value_len, + void *argp), + void *argp); + +// Returns whether the peer enabled HTTP/3 DATAGRAM frame support. +bool quiche_h3_dgram_enabled_by_peer(quiche_h3_conn *conn, + quiche_conn *quic_conn); + +// Writes data to the DATAGRAM send queue. +ssize_t quiche_h3_send_dgram(quiche_h3_conn *conn, quiche_conn *quic_conn, + uint64_t flow_id, uint8_t *data, size_t data_len); + +// Reads data from the DATAGRAM receive queue. +ssize_t quiche_h3_recv_dgram(quiche_h3_conn *conn, quiche_conn *quic_conn, + uint64_t *flow_id, size_t *flow_id_len, + uint8_t *out, size_t out_len); + +// Frees the HTTP/3 connection object. +void quiche_h3_conn_free(quiche_h3_conn *conn); + +#if defined(__cplusplus) +} // extern C +#endif + +#endif // QUICHE_H diff -r d8f806f1d327 -r c520966de7fa ui.lisp --- a/ui.lisp Sun May 14 21:27:04 2023 -0400 +++ b/ui.lisp Mon May 15 21:40:24 2023 -0400 @@ -1,6 +1,29 @@ (in-package :demo) + +(defparameter ui-server-port 8080) +(defparameter ui-server-host "0.0.0.0") + +(defclass ui-element (clog-element) () + (:documentation "UI Element Object.")) + +(defgeneric create-ui-element (obj &key hidden class id mode) + (:documentation "Create a new ui-element as a child of OBJ.")) +(defmethod create-ui-element ((obj clog:clog-obj) + &key (class nil) + (hidden nil) + (id nil) + (mode 'auto)) + (let ((new (clog:create-div obj + :class class + :hidden hidden + :id id + :mode mode))) + (clog:set-geometry new :width 200 :height 100) + (change-class new 'ui-element))) + (defun on-new-window (body) "Handle new window event." + (clog:debug-mode body) (let ((elt (clog:create-child body "

foobar

"))) (clog:set-on-click elt @@ -9,5 +32,12 @@ (defun start-ui () "Start the UI." - (clog:initialize #'on-new-window) + (clog:initialize #'on-new-window + :extended-routing t + :host ui-server-host + :port ui-server-port) (clog:open-browser)) + +(defun stop-ui () + "Stop the UI." + (clog:shutdown)) diff -r d8f806f1d327 -r c520966de7fa www/boot.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/boot.html Mon May 15 21:40:24 2023 -0400 @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff -r d8f806f1d327 -r c520966de7fa www/bootstrap.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/bootstrap.html Mon May 15 21:40:24 2023 -0400 @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff -r d8f806f1d327 -r c520966de7fa www/css/w3.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/css/w3.css Mon May 15 21:40:24 2023 -0400 @@ -0,0 +1,235 @@ +/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */ +html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit} +/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */ +html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0} +article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item} +audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline} +audio:not([controls]){display:none;height:0}[hidden],template{display:none} +a{background-color:transparent}a:active,a:hover{outline-width:0} +abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted} +b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000} +small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} +sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none} +code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible} +button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold} +button,input{overflow:visible}button,select{text-transform:none} +button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button} +button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0} +button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText} +fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em} +legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto} +[type=checkbox],[type=radio]{padding:0} +[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto} +[type=search]{-webkit-appearance:textfield;outline-offset:-2px} +[type=search]::-webkit-search-decoration{-webkit-appearance:none} +::-webkit-file-upload-button{-webkit-appearance:button;font:inherit} +/* End extract */ +html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden} +h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px} +.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace} +h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px} +hr{border:0;border-top:1px solid #eee;margin:20px 0} +.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit} +.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc} +.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1} +.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1} +.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center} +.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top} +.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px} +.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap} +.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)} +.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} +.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none} +.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none} +.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%} +.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none} +.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block} +.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s} +.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%} +.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc} +.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer} +.w3-dropdown-hover:hover .w3-dropdown-content{display:block} +.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000} +.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000} +.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1} +.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px} +.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto} +.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%} +.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%} +.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px} +.w3-main,#main{transition:margin-left .4s} +.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)} +.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px} +.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto} +.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0} +.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left} +.w3-bar .w3-button{white-space:normal} +.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0} +.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%} +.w3-responsive{display:block;overflow-x:auto} +.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before, +.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both} +.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%} +.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%} +.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%} +.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%} +@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%} +.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%} +.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}} +@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%} +.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%} +.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}} +.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px} +.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px} +.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell} +.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom} +.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important} +@media (max-width:1205px){.w3-auto{max-width:95%}} +@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px} +.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative} +.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center} +.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}} +@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}} +@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}} +@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}} +@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}} +.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0} +.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2} +.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0} +.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0} +.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)} +.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)} +.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)} +.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} +.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} +.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none} +.w3-display-position{position:absolute} +.w3-circle{border-radius:50%} +.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px} +.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px} +.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px} +.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px} +.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word} +.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%} +.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)} +.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)} +.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}} +.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}} +.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}} +.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}} +.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}} +.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}} +.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}} +.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}} +.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important} +.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1} +.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75} +.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)} +.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)} +.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)} +.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important} +.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important} +.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important} +.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important} +.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important} +.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important} +.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important} +.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important} +.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important} +.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important} +.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important} +.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important} +.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important} +.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important} +.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important} +.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important} +.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important} +.w3-left{float:left!important}.w3-right{float:right!important} +.w3-button:hover{color:#000!important;background-color:#ccc!important} +.w3-transparent,.w3-hover-none:hover{background-color:transparent!important} +.w3-hover-none:hover{box-shadow:none!important} +/* Colors */ +.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important} +.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important} +.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important} +.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important} +.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important} +.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important} +.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important} +.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important} +.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important} +.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important} +.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important} +.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important} +.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important} +.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important} +.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important} +.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important} +.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important} +.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important} +.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important} +.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important} +.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important} +.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important} +.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important} +.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important} +.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important} +.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important} +.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important} +.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important} +.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important} +.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important} +.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important} +.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important} +.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important} +.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important} +.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important} +.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important} +.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important} +.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important} +.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important} +.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important} +.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important} +.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important} +.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important} +.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important} +.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important} +.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important} +.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important} +.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important} +.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important} +.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important} +.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important} +.w3-text-white,.w3-hover-text-white:hover{color:#fff!important} +.w3-text-black,.w3-hover-text-black:hover{color:#000!important} +.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important} +.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important} +.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important} +.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important} +.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important} +.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important} +.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important} +.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important} +.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important} +.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important} +.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important} +.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important} +.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important} +.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important} +.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important} +.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important} +.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important} +.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important} +.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important} +.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important} +.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important} +.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important} +.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important} +.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important} +.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important} +.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important} +.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important} +.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important} +.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important} +.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important} +.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important} diff -r d8f806f1d327 -r c520966de7fa www/favicon.ico diff -r d8f806f1d327 -r c520966de7fa www/robots.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/robots.txt Mon May 15 21:40:24 2023 -0400 @@ -0,0 +1,2 @@ +User-agent: * +Disallow: