package lsp

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file io.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
open Import

exception Error of string

let () =
  Printexc.register_printer (function
      | Error msg -> Some ("Error: " ^ msg)
      | _ -> None)

let caseless_equal a b =
  if a == b then true
  else
    let len = String.length a in
    len = String.length b
    &&
    let stop = ref false in
    let idx = ref 0 in
    while (not !stop) && !idx < len do
      let c1 = String.unsafe_get a !idx in
      let c2 = String.unsafe_get b !idx in
      if Char.lowercase_ascii c1 <> Char.lowercase_ascii c2 then stop := true;
      incr idx
    done;
    not !stop

let content_type_lowercase =
  String.lowercase_ascii Header.Private.Key.content_type

let content_length_lowercase =
  String.lowercase_ascii Header.Private.Key.content_length

module Make (Io : sig
  type 'a t

  val return : 'a -> 'a t

  val raise : exn -> 'a t

  module O : sig
    val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t

    val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
  end
end) (Chan : sig
  type input

  type output

  val read_line : input -> string option Io.t

  val read_exactly : input -> int -> string option Io.t

  val write : output -> string -> unit Io.t
end) =
struct
  open Io.O

  let read_header =
    let init_content_length = -1 in
    let rec loop chan content_length content_type =
      let* line = Chan.read_line chan in
      match line with
      | None -> Io.return None
      | Some "" | Some "\r" -> Io.return (Some (content_length, content_type))
      | Some line -> (
        match String.lsplit2 ~on:':' line with
        | None -> loop chan content_length content_type
        | Some (k, v) ->
          let k = String.trim k in
          if
            caseless_equal k content_length_lowercase
            && content_length = init_content_length
          then
            let content_length = int_of_string_opt (String.trim v) in
            match content_length with
            | None -> Io.raise (Error "Content-Length is invalid")
            | Some content_length -> loop chan content_length content_type
          else if caseless_equal k content_type_lowercase && content_type = None
          then
            let content_type = String.trim v in
            loop chan content_length (Some content_type)
          else loop chan content_length content_type)
    in
    fun chan ->
      let open Io.O in
      let* res = loop chan init_content_length None in
      match res with
      | None -> Io.return None
      | Some (content_length, content_type) ->
        let+ () =
          if content_length = init_content_length then
            Io.raise (Error "content length absent")
          else Io.return ()
        in
        Some (Header.create ?content_type ~content_length ())

  let read chan =
    let* header = read_header chan in
    match header with
    | None -> Io.return None
    | Some header -> (
      let len = Header.content_length header in
      let* buf = Chan.read_exactly chan len in
      match buf with
      | None -> Io.raise (Error "unable to read json")
      | Some buf ->
        let json = Json.of_string buf in
        Io.return (Some (Jsonrpc.Packet.t_of_yojson json)))

  let write chan packet =
    let json = Jsonrpc.Packet.yojson_of_t packet in
    let data = Json.to_string json in
    let content_length = String.length data in
    let header = Header.create ~content_length () in
    let* () = Chan.write chan (Header.to_string header) in
    Chan.write chan data
end
OCaml

Innovation. Community. Security.