package lpd

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

Source file socket.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
(* buffered socket abstraction
 **********************************************************************
 * © Christophe Troestler
 *
 * This library is distributed under the terms of the GNU Lesser
 * General Public License, with the special exception on linking as
 * for the OCaml Library.
 *)
(* Since (in|out)_channel_of_descr does NOT work under win32, we need
   to implement a minimal buffered interface. *)

(* Specialize [min] to integers for performance reasons (> 150% faster). *)
let min x y = if (x:int) <= y then x else y

let buffer_len = 4096

type out_channel = {
  out_fd : Unix.file_descr;
  out_buf : Bytes.t;  (* The data to output is in out_buf.[i], 0 <= i < out1 *)
  mutable out1 : int;(* out1 <= buffer_len ; < 0 iff channel closed. *)
}

let out_channel_of_descr fd = {
  out_fd = fd;
  out_buf = Bytes.create buffer_len;  out1 = 0; }

let descr_of_out_channel outchan = outchan.out_fd

type in_channel = {
  in_fd : Unix.file_descr;
  in_buf : Bytes.t;   (* The data in the in_buf is at indexes i s.t. *)
  mutable in0 : int; (* in0 <= i < in1. *)
  mutable in1 : int; (* Invariant: 0 <= in0 ; in1 <= buffer_len
                        in1 < 0 indicates a closed channel. *)
}

let in_channel_of_descr fd = {
  in_fd = fd;
  in_buf = Bytes.create buffer_len; in0 = 0; in1 = 0 }

let descr_of_in_channel inchan = inchan.in_fd

(*
 * Output functions
 *)

(* [flush_noerr oc] assumes that the channel is not closed,
   i.e. [oc.out1 >= 0] *)
let flush_noerr oc =
  let rec write i0 len =
    let w = Unix.write oc.out_fd oc.out_buf i0 len in
    if w < len then write (i0 + w) (len - w) in
  write 0 oc.out1;
  oc.out1 <- 0

let flush oc =
  if oc.out1 >= 0 then flush_noerr oc (* Channel [oc] not closed. *)

let close_out oc =
  if oc.out1 >= 0 then begin
    (* Not already closed *)
    flush_noerr oc;
    Unix.close oc.out_fd;
    oc.out1 <- -1
  end

let rec unsafe_output oc buf pos len =
  let w = min len (buffer_len - oc.out1) in
  Bytes.blit buf pos oc.out_buf oc.out1 w;
  oc.out1 <- oc.out1 + w;
  if w < len then begin
    flush_noerr oc;
    unsafe_output oc buf (pos + w) (len - w)
  end

let output oc buf pos len =
  if pos < 0 || len < 0 || pos + len > Bytes.length buf
  then invalid_arg "Socket.output";
  if oc.out1 < 0 then raise(Sys_error "Bad file descriptor");
  unsafe_output oc buf pos len

let output_string oc s =
  if oc.out1 < 0 then raise(Sys_error "Bad file descriptor");
  unsafe_output oc (Bytes.unsafe_of_string s) 0 (String.length s)

let output_char oc c =
  if oc.out1 < 0 then raise(Sys_error "Bad file descriptor");
  if oc.out1 = buffer_len then flush_noerr oc;
  Bytes.unsafe_set oc.out_buf oc.out1 c;
  oc.out1 <- oc.out1 + 1

let fprintf oc =
  Printf.kprintf (fun s -> output_string oc s)


(*
 * Input functions
 *)

let close_in chan =
  if chan.in1 >= 0 then begin
    (* [chan] not yet closed *)
    Unix.close chan.in_fd;
    chan.in0 <- 0;
    chan.in1 <- -1
  end

(* [fill_in_buf chan] refills in_buf if needed (when empty).  After this
   [in0 < in1] or [in1 = 0], the latter indicating that the end of
   file is reached (and then in0 = 0). *)
let fill_in_buf chan =
  if chan.in0 >= chan.in1 then begin
    chan.in0 <- 0;
    try
      chan.in1 <- Unix.read chan.in_fd chan.in_buf 0 buffer_len;
    with
    | Unix.Unix_error(Unix.EAGAIN, _, _)
    | Unix.Unix_error(Unix.EWOULDBLOCK, _, _) -> raise Sys_blocked_io
  end

let unsafe_input chan buf ofs len =
  fill_in_buf chan;
  let r = min len (chan.in1 - chan.in0) in
  Bytes.blit chan.in_buf chan.in0 buf ofs r;
  chan.in0 <- chan.in0 + r;
  r

let input ic buf ofs len =
  if ofs < 0 || len < 0 || ofs + len > Bytes.length buf
  then invalid_arg "Socket.input";
  if ic.in1 < 0 then raise(Sys_error "Bad file descriptor");
  unsafe_input ic buf ofs len

let input_char ic =
  if ic.in1 < 0 then raise(Sys_error "Bad file descriptor");
  fill_in_buf ic;
  if ic.in1 = 0 then raise End_of_file
  else
    let c = Bytes.unsafe_get ic.in_buf ic.in0 in
    ic.in0 <- ic.in0 + 1;
    c


let rec unsafe_really_input ic s ofs len =
  if len > 0 then begin
    let r = unsafe_input ic s ofs len in
    if r = 0 then raise End_of_file
    else unsafe_really_input ic s (ofs+r) (len-r)
  end

let really_input ic s ofs len =
  if ofs < 0 || len < 0 || ofs + len > Bytes.length s
  then invalid_arg "Socket.really_input";
  if ic.in1 < 0 then raise(Sys_error "Bad file descriptor");
  unsafe_really_input ic s ofs len



(* [index_in_range i0 i1 c s] returns the index [j] of the first
   occurrence of [c] in s.[i], i0 <= i < i1.  If no occurence of [c]
   is found, it will return [i1].  It is assumed that 0 <= i0 and i1
   <= String.length s. *)
let index_in_range i0 i1 c s =
  let rec examine i =
    if i < i1 then
      if Bytes.unsafe_get s i = c then i
      else examine (i+1)
    else i1 in
  examine i0


let input_till c ic buf ofs len =
  if ofs < 0 || len < 0 || ofs + len > Bytes.length buf
  then invalid_arg "Socket.input_till";
  if ic.in1 < 0 then raise(Sys_error "Bad file descriptor");
  fill_in_buf ic;
  if ic.in1 = 0 then raise End_of_file;
  let in1 = min (ic.in0 + len) ic.in1 in
  let i = index_in_range ic.in0 in1 c ic.in_buf in
  let r = i - ic.in0 in
  Bytes.blit ic.in_buf ic.in0 buf ofs r;
  ic.in0 <- i;
  r

let rec input_till_char acc c ic =
  fill_in_buf ic;
  if ic.in1 = 0 then
    if acc = Bytes.empty then raise End_of_file else acc
  else begin
    (* Buffer contains something (ic.in0 < ic.in1), seek [c]. *)
    let i = index_in_range ic.in0 ic.in1 c ic.in_buf in
    (* FIXME: use a buffer: *)
    let line = Bytes.cat acc (Bytes.sub ic.in_buf ic.in0 (i - ic.in0)) in
    ic.in0 <- i + 1; (* skip [c] *)
    if i = ic.in1 then input_till_char line c ic
    else line
  end

let input_line ic =
  if ic.in1 < 0 then raise(Sys_error "Bad file descriptor");
  Bytes.unsafe_to_string(input_till_char Bytes.empty '\n' ic)

let input_all_till c ic =
  if ic.in1 < 0 then raise(Sys_error "Bad file descriptor");
  Bytes.unsafe_to_string(input_till_char Bytes.empty c ic)


(* Define an [open_connection] alike the one in the Unix lib for
   convenience. *)
let open_connection sockaddr =
  let sock =
    Unix.socket (Unix.domain_of_sockaddr sockaddr) Unix.SOCK_STREAM 0 in
  try
    Unix.connect sock sockaddr;
    (in_channel_of_descr sock, out_channel_of_descr sock)
  with exn ->
    Unix.close sock; raise exn

let shutdown_connection ic =
  Unix.shutdown (descr_of_in_channel ic) Unix.SHUTDOWN_SEND


(* Polling *)

let select inl outl t =
  (* Gather input channels whose buffer is not empty *)
  let (inbuf, inempty) = List.partition (fun c -> c.in0 < c.in1) inl in
  (* Check the file descriptors *)
  let inempty_fd = List.map (fun c -> c.in_fd) inempty in
  let out_fd = List.map (fun c -> c.out_fd) outl in
  let (in_ready, out_ready, _) = Unix.select inempty_fd out_fd [] t in
  (* Gather the results *)
  (inbuf @ List.filter (fun c -> List.mem c.in_fd in_ready) inempty,
   List.filter (fun c -> List.mem c.out_fd out_ready) outl)


(* I/O object *)

(* FIXME: Sys_blocked_io / EAGAIN ??? *)
class out_channel_obj chan_init =
object
  val chan = chan_init

  method output buf pos len =
    output chan buf pos len;
    len

  method flush () = flush chan
  method close_out () = close_out chan

  method output_string = output_string chan
  method output_char = output_char chan
  method fprintf : 'a. ('a, unit, string, unit) format4 -> 'a
    = fprintf chan
end

class in_channel_obj chan_init =
object
  val chan = chan_init

  method input = input chan
  method close_in() = close_in chan

  method input_char () = input_char chan
  method really_input = really_input chan
  method input_till c = input_till c chan
  method input_line () = input_line chan
  method input_all_till c = input_all_till c chan
end
OCaml

Innovation. Community. Security.