This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,193 @@
module type S = sig
type stack
(** The type of the TCP/IP stack. *)
type ipaddr
(** The type of the IP address. *)
(** {2 Protocols.}
From the given stack, [Paf_mirage] constructs protocols needed for HTTP:
- A simple TCP/IP protocol
- A TCP/IP protocol wrapped into TLS {i via} [ocaml-tls]
We expose these protocols in the sense of [mimic]. They are registered
globally with [mimic] and are usable {i via} [mimic] (see
{!Mimic.resolve}) as long as the given [ctx] contains {!val:tcp_edn}
and/or {!val:tls_edn}. Such way to instance {i something} which represents
these protocols and usable as a {!Mirage_flow.S} are useful for the
client-side, see {!val:run}.
We expose 2 new functions: [no_close]/[to_close]. In a specific context
such as the proxy, the handler should notify us to fakely close the
underlying connection. Indeed, [Paf] will try to close your connection as
soon as the HTTP transmission is finished. However, in the case of a
proxy, the connection must remains then. {!val:TCP.no_close} sets the
[flow] so that the next call to {!val:TCP.close} is ignored.
{!val:to_close} resets the [flow] to the basic behavior - we will really
close the given [flow]. *)
module TCP : sig
include Mirage_flow.S
val dst : flow -> ipaddr * int
val no_close : flow -> unit
val to_close : flow -> unit
end
module TLS : sig
type error =
[ `Tls_alert of Tls.Packet.alert_type
| `Tls_failure of Tls.Engine.failure
| `Read of TCP.error
| `Write of TCP.write_error ]
type write_error = [ `Closed | error ]
include
Mirage_flow.S with type error := error and type write_error := write_error
val no_close : flow -> unit
val to_close : flow -> unit
val epoch : flow -> (Tls.Core.epoch_data, unit) result
val reneg :
?authenticator:X509.Authenticator.t ->
?acceptable_cas:X509.Distinguished_name.t list ->
?cert:Tls.Config.own_cert ->
?drop:bool ->
flow ->
(unit, [ write_error | `Msg of string ]) result Lwt.t
val key_update :
?request:bool ->
flow ->
(unit, [ write_error | `Msg of string ]) result Lwt.t
val server_of_flow :
Tls.Config.server -> TCP.flow -> (flow, write_error) result Lwt.t
val client_of_flow :
Tls.Config.client ->
?host:[ `host ] Domain_name.t ->
TCP.flow ->
(flow, write_error) result Lwt.t
end
val tcp_protocol : (stack * ipaddr * int, TCP.flow) Mimic.protocol
val tcp_edn : (stack * ipaddr * int) Mimic.value
val tls_edn :
([ `host ] Domain_name.t option * Tls.Config.client * stack * ipaddr * int)
Mimic.value
val tls_protocol :
( [ `host ] Domain_name.t option * Tls.Config.client * stack * ipaddr * int,
TLS.flow )
Mimic.protocol
(** {2 Server implementation.} *)
type t
(** The type of the {i socket} bound on a specific port (via {!init}). *)
type dst = ipaddr * int
val init : port:int -> stack -> t Lwt.t
(** [init ~port stack] bounds the given [stack] to a specific port and return
the main socket {!t}. *)
val accept : t -> (TCP.flow, [> `Closed ]) result Lwt.t
(** [accept t] waits an incoming connection and return a {i socket} connected
to a peer. *)
val close : t -> unit Lwt.t
(** [close t] closes the main {e socket}. *)
(** {3 HTTP/1.1 servers.}
The user is able to launch a simple HTTP/1.1 server with TLS or not.
Below, you can see a simple example:
{[
let run ~error_handler ~request_handler =
Paf_mirage.init ~port:8080 stack >>= fun t ->
Paf_mirage.http_service ~error_handler request_handler
>>= fun service ->
let (`Initialized th) = Paf_mirage.serve service t in
th
]} *)
val http_service :
?config:H1.Config.t ->
error_handler:(dst -> H1.Server_connection.error_handler) ->
(TCP.flow -> dst -> H1.Server_connection.request_handler) ->
t Paf.service
(** [http_service ~error_handler request_handler] makes an HTTP/AF service
where any HTTP/1.1 requests are handled by [request_handler]. The returned
service is not yet launched (see {!serve}). *)
val https_service :
tls:Tls.Config.server ->
?config:H1.Config.t ->
error_handler:(dst -> H1.Server_connection.error_handler) ->
(TLS.flow -> dst -> H1.Server_connection.request_handler) ->
t Paf.service
(** [https_service ~tls ~error_handler request_handler] makes an HTTP/AF
service over TLS (from the given TLS configuration). Then, HTTP/1.1
requests are handled by [request_handler]. The returned service is not yet
launched (see {!serve}). *)
(** {3 HTTP/1.1 & H2 over TLS server.}
It's possible to make am ALPN server. It's an HTTP server which can handle
- HTTP/1.1 requests
- and H2 requests
The choice is made by the ALPN challenge on the TLS layer where the client
can send which protocol he/she wants to use. Therefore, the server must
handle these two cases. *)
val alpn_service :
tls:Tls.Config.server ->
?config:H1.Config.t * H2.Config.t ->
(TLS.flow, dst) Alpn.server_handler ->
t Paf.service
(** [alpn_service ~tls handler] makes an H2/HTTP/AF service over TLS (from the
given TLS configuration). An HTTP request (version 1.1 or 2) is handled
then by [handler]. The returned service is not yet launched (see
{!val:serve} to launch it). *)
val serve :
?stop:Lwt_switch.t -> 't Paf.service -> 't -> [ `Initialized of unit Lwt.t ]
(** [serve ?stop service] returns an initialized promise of the given service
[service]. [stop] can be used to stop the service. *)
end
module Make (Stack : Tcpip.Tcp.S) :
S with type stack = Stack.t and type ipaddr = Stack.ipaddr
(** {2 Client implementation.}
The client implementation of [Paf_mirage] does not strictly need a
{i functor}. Indeed, the client was made in the sense of [mimic]. The user
should provide a {!Mimic.ctx} which generate a {!paf_transmission}. By this
way, the {!run} function is able to introspect the used protocol (regardless
its implementation) and do the ALPN challenge with the server. *)
type transmission = [ `Clear | `TLS of string option ]
val paf_transmission : transmission Mimic.value
val run :
ctx:Mimic.ctx ->
(Ipaddr.t * int) option Alpn.client_handler ->
[ `V1 of H1.Request.t | `V2 of H2.Request.t ] ->
(Alpn.alpn_response, [> Mimic.error ]) result Lwt.t
(** [run ~ctx handler req] sends an HTTP request (H2 or HTTP/1.1) to a peer
which can be reached {i via} the given Mimic's [ctx]. If the connection is
recognized as a {!tls_protocol}, we proceed an ALPN challenge between what
the user chosen and what the peer can handle. Otherwise, we send a simple
HTTP/1.1 request or a [h2c] request. *)