0

I am trying to write an http client in OCaml:

module Connection = struct
    let sock_fd =
        let s_fd = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
        Unix.setsockopt s_fd Unix.TCP_NODELAY true;
        s_fd

    let read_from_sock () =
        let buffer = Bytes.create 512 in
        let rec read_all request buffer =
            let r = Unix.read sock_fd buffer 0 512 in
            if r < 512 then request ^ buffer
            else read_all (request ^ buffer) buffer in
        read_all "" buffer

    let write_to_sock str =
        let len = String.length str in
        let _ = Unix.write sock_fd str 0 len in ()

    let make_request request serv_addr =
        Unix.connect sock_fd serv_addr;
        write_to_sock request

    class connection address port =
        object
            val serv_addr = Unix.ADDR_INET (Unix.inet_addr_of_string address, port)

            method get_response request =
                let _ = make_request request serv_addr in
                let rec wait_for_response () =
                    let response = read_from_sock () in
                    match String.length response with
                    | 0 -> wait_for_response ()
                    | _ -> Printf.printf "%s\n" response in
                wait_for_response ();
                Unix.shutdown sock_fd Unix.SHUTDOWN_ALL;
                Unix.close sock_fd
        end

    let create address port = new connection address port
end

let connection = Connection.create "54.175.219.8" 80;;
connection#get_response "GET / HTTP/1.1\r\n"

The program simply hangs until it crashes when the connection is reset during the read. The funky part is, no other errors are thrown. Since OCaml doesn't return an int response like C, I'd assume if I did something wrong connecting or creating the socket that the runtime would throw an error.

If you're not familiar with OCaml, I'd assume the (extremely rough) pseudo-C equivalent of what I'm doing is this (perhaps this will help the debugging):

int sock_fd = socket(PF_INET, SOCK_STREAM);
setsockopt(sock_fd, TCP_NODELAY, 1);

serv_addr addr {"54.175.219.8", 80};
connect(sock_fd, &serv_addr);
write(sock_fd, "GET / HTTP/1.1\r\n");

char buffer[512];
while (buffer = read(sock_fd)) {
    printf("%s\n", buffer);
}

shutdown(sock_fd, SHUTDOWN_ALL);
close(sock_fd);

If that is not helpful, ignore it. I just figured I'd summarize in pseudo-code. Are there any glaring issues? (Aside from not reading the int response from socket/connect/write in the C because OCaml's runtime should? take care of throwing errors here.)

eatonphil
  • 13,115
  • 27
  • 76
  • 133

1 Answers1

2

In HTTP protocol request should end up with two empty lines, not one (in a layman terms). In your case, server just waits for the second "enter", and your client blocks until it receives the response. Just add extra \r\n to your request and everything will work... better. At least you will get a response. But your code contains some glitches, so do expect that something goes wrong.

Just for your information, there're many good libraries for networking in OCaml. The best, to my opinion, is cohttp. Also, you may find interesting mine socket library, that is a little bit more low-level than cohttp

ivg
  • 34,431
  • 2
  • 35
  • 63
  • Good call! And you're right it was an exception not an error. My fault there. Thanks for the library tips too, I just enjoy messing around without for education's sake. – eatonphil Feb 23 '15 at 02:58
  • then I would also suggest to read this excellent [book](http://ocaml.github.io/ocamlunix/). It is very low-level, but it looks like that this won't scare you – ivg Feb 23 '15 at 03:02