如何在Erlang中读取http POST请求的主体

pgx2nnw8  于 2022-12-08  发布在  Erlang
关注(0)|答案(1)|浏览(186)

I'm working on a server implementation in Erlang that has a receive loop in which it handles HTTP requests. After opening listen socket and spawning acceptor, server is ready for receiving connections. TLS handshake is done, and client is connected to server and starts sending HTTPS requests.
Server's receiving part uses ssl:recv/2 for reading incoming requests and the socket is setup so that packets are in the form of HttpPacket . So far the handling of 'GET' request works fine, but I need to include 'POST' requests. Reading 'POST' request also works, but I can't seem to read the body of the request.

  1. How am I supposed to read the body of incoming request?
  2. Is it possible to read it with ssl:recv/2?
  3. Should the socket expect or format data in a different form?
  4. Would you recommend something else?
    I've tried using ssl:recv/2 and regular receive . Using receive freezes the server until it times out.
case ssl:recv(Tls_socket, 0) of
    {ok, {http_request, 'POST', Data, Vsn}} ->
        do_something ...

Data ends up being {abs_path, <<"/example/ex">>} . I expected data to be the body of the request.
I couldn't read the data because socket was set to package data as HttpPacket (i.e., any() ). Reading with no packaging helped, so only thing left to do was to parse the query.

u4vypkhs

u4vypkhs1#

The HTTP parser built in to Erlang parses one line at a time. So first you'd call ssl:recv , and receive {http_request, 'POST', Path, Vsn} , as in your example above. This corresponds to the line POST /example/ex HTTP/1.1 in the HTTP request. Then you'd call ssl:recv over and over, receiving {http_header, _, Field, _, Value} , one for each header in the request, until it returns http_eoh , meaning "end of headers".
At this point, you should have received a Content-Length header, so you know how many bytes to read from the socket. Switch the socket into "raw" mode (as opposed to HTTP mode):

ssl:setopts(Tls_socket, [{packet, raw}]),

And receive the number of bytes you need:

{ok, RequestBody} = ssl:recv(Tls_socket, ContentLength),

And send the response.
If you want to receive another HTTP request on the same socket, switch it back into HTTP mode:

ssl:setopts(Tls_socket, [{packet, http}]),

You mentioned receive timing out. This depends on whether the socket is in "active" or "passive" mode. In "passive" mode, you need to call ssl:recv to receive data, while in "active" mode, incoming data will be sent to the process that own the socket as messages.
You can switch on active mode by passing the option {active, true} , but that runs the risk of your process receiving incoming data faster than it can handle it. Usually you'd set the option {active, once} , meaning that the socket sends one message to the process and then reverts to passive mode, requiring you to set the option {active, once} again in order to receive more data.

相关问题