Module Http.Path

Absolute paths.

Absolute paths

type t = string list

The type for absolute URI paths represented as non-empty lists of percent-decoded path segments:

  • The empty list denotes the absence of a path.
  • Path segments can be empty "".
  • The root path / is represented by [""].
  • The path /a is represented by ["a"], see more examples here.

Warning. You should never concatenate these segments with a separator to get a file path: they may contain stray percent-decoded directory separators. Use the function Path.to_absolute_filepath to interpret paths as file paths.

val none : t

none is the path [] for when there is none.

val root : t

root is the root path [""].

Operations

val undot_and_compress : t -> t

undot_and_compress p removes "." and ".." according to the RFC 3986 algorithm and suppresses non-final empty "" segments.

val strip_prefix : prefix:t -> t -> t

strip_prefix ~prefix p removes the prefix path prefix from p.

If prefix ends with an empty segment, it matches any corresponding segment at that point so that stripping /a/ from /a/b results in /b. However stripping /a/ from /a yields [] (none).

If p is not prefixed by prefix, or if any of prefix or p is [] (none), [] is returned.

Given a path p and the same path p' with a trailing slash, the set of paths prefixed by p is the the set of path prefixed by p' plus p itelf. Stripping p to itself yields root (see here for why we think that's desirable).

A few examples basic edge cases root and none:

  • strip_prefix [""] (_ :: _ as p) = p
  • strip_prefix (_ :: _ as p) p = [""]
  • strip_prefix _ [] -> []
  • strip_prefix [] _ = []

Stripping a prefix /a:

  • strip_prefix ["a"] [""] = []
  • strip_prefix ["a"] ["a"] = [""]
  • strip_prefix ["a"] ["a"; ""] = [""]
  • strip_prefix ["a"] ["b"] = []
  • strip_prefix ["a"] ["a"; "b"] = ["b"]
  • strip_prefix ["a"] ["a"; "b"; ""] = ["b"; ""]
  • strip_prefix ["a"] ["a"; ""; "b"] = [""; "b"]

Stripping a prefix /a/:

  • strip_prefix ["a"; ""] [""] = []
  • strip_prefix ["a"; ""] ["a"] = []
  • strip_prefix ["a"; ""] ["b"] = []
  • strip_prefix ["a"; ""] ["a"; ""] = [""]
  • strip_prefix ["a"; ""] ["a"; "b"] = ["b"]
  • strip_prefix ["a"; ""] ["a"; "b"; ""] = ["b"; ""]
  • strip_prefix ["a"; ""] ["a"; ""; "b"] = [""; "b"]
val concat : t -> t -> t

concat p0 p1 concatenates p0 and p1. If p0 ends with an empty segment and p1 is not none that empty segment is dropped. A few examples:

  • concat p [] = p
  • concat [] p = p
  • concat [""] [""] = [""]
  • concat [""] ["a"; "b"] = ["a"; "b"]
  • concat ["a"] [""] = ["a"; ""]
  • concat ["a"; ""] [""] = ["a"; ""]
  • concat ["a"; "b"] ["c"; "d"] = ["a"; "b"; "c"; "d"]
  • concat ["a"; "b"; ""] ["c"; "d"] = ["a"; "b"; "c"; "d"]
  • concat ["a"; "b"; ""] [""] = ["a"; "b"; ""]
  • concat ["a"; "b"; ""] [""; "c"] = ["a"; "b"; ""; "c"]
val relative : src:t -> dst:t -> t

relative ~src ~dst is the relative path rel that goes from absolute src to absolute dst. This means that undot_and_compress (concat src rel) should yield dst.

Warning. This function assumes both src and dst have no relative or empty path components. If needed use undot_and_compress to ensure that.

File paths

type fpath = string

The type for file paths.

val has_dir_seps : string -> bool

has_dir_seps s is true iff s contains a '/' or a '\\' character.

val to_absolute_filepath : t -> (fpath, string) Stdlib.result

to_absolute_filepath p is an absolute file path for undot_and_compress p. Errors if any of the path segments contains a stray slash or backslash or if p is the empty list. The result always uses / as a directory separator regardless of the platform and is guaranteed to be free of any . or .. segments.

val prefix_filepath : prefix:fpath -> fpath -> fpath

prefix_filepath ~prefix p prefixes p by prefix avoiding introducing empty segments. This function assumes / is the directory separator regardless of the platform.

val filepath_ext : fpath -> string

filepath_ext p is the file extension of file path p. This function assumes / is the directory separator regardless of the platform.

Converting

val encode : t -> string

encode p encodes an absolute-path for p as follows:

  1. In each segment percent-encode any byte that is not unreserved, sub-delims, ':' or '@' to produce a valid URI segment.
  2. Prepends each segment with a '/'.
  3. Concatenate the result.

The empty list is special cased and yields "". This is for encoding HTTP paths, use to_absolute_filepath to convert paths to file paths.

Here are a few examples:

  • encode [] = ""
  • encode [""] = "/"
  • encode [""; ""] = "//"
  • encode [""; "a"] = "//a"
  • encode ["a";"b";"c"] = "/a/b/c"
  • encode ["a";"b";"";"c";] = "/a/b//c"
  • encode ["a";"b";"c";""] = "/a/b/c/"
  • encode ["a";"b";"c";" "] = "/a/b/c/%20"
  • encode ["a";"b";"c";"";""] = "/a/b/c//"
  • encode ["a"; "b/"; "c"] = "/a/b%2F/c"
  • encode ["r\xC3\xC9volte"] = "/r%C3%C9volte"
  • encode ["a"; "not%20"; "b"] = "/a/not%2520/b"
val decode : string -> (t, string) Stdlib.result

decode s decodes an absolute-path to its percent-decoded list of segments. By definition of absolute-path the list of segments is never empty.

Here are a few examples:

  • decode "/" = Ok [""]
  • decode "//" = Ok ["";""]
  • decode "//a" = Ok ["";"a"]
  • decode "/a/b/c" = Ok ["a";"b";"c"]
  • decode "/a/b//c" = Ok ["a";"b";"";"c"]
  • decode "/a/b/c/" = Ok ["a";"b";"c";""]
  • decode "/a/b/c/%20" = Ok ["a";"b";"c";" "]
  • decode "/a/b//c//" = Ok ["a";"b";"";"c";"";""]
  • decode "/a/b%2F/c" = Ok ["a"; "b/"; "c"]
  • decode "/r%C3%C9volte" = Ok ["r\xC3\xC9volte"]
  • decode "/a/not%2520/b" = Ok ["a"; "not%20"; "b"]
  • decode "" = Error _
  • decode "a/b/c" = Error _
val and_query_string_of_request_target : string -> (t * string option, string) Stdlib.result

and_query_string_of_request_target s parses a path and a query string (without the '?') form the request target s (which can be an URL).

val pp : Stdlib.Format.formatter -> t -> unit

pp formats paths for inspection.

val pp_dump : Stdlib.Format.formatter -> t -> unit

pp_dump formats paths for deeper inspection..