Where to save request header info on Codec with tokio_proto?


#1

I am looking at the example of a Tokio Proto Server in the following link - https://tokio.rs/docs/getting-started/simple-server/ , specifically the fn decode method:

impl Decoder for LineCodec {
    type Item = String;
    type Error = io::Error;

    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<String>> {
        if let Some(i) = buf.iter().position(|&b| b == b'\n') {
            // remove the serialized frame from the buffer.
            let line = buf.split_to(i);

            // Also remove the '\n'
            buf.split_to(1);

            // Turn this data into a UTF string and return it in a Frame.
            match str::from_utf8(&line) {
                Ok(s) => Ok(Some(s.to_string())),
                Err(_) => Err(io::Error::new(io::ErrorKind::Other,
                                             "invalid UTF-8")),
            }
        } else {
            Ok(None)
        }
    }

In this example, there are no request headers, the Codec is simply trying to find a delimiter (’\n’) at the end of a request. However, for my usecase, each request that will be decoded will have a header, which in turn will indicate the actual length of the request body.

Since parsing the header itself requires processing, I would prefer not to do it ever single time decode is called while I am receiving bytes. I am not sure what the best practice would be here. Would it make sense to add a field on LineCodec, like cur_header: Option<Header>, and refer to it while using the decode method? Would this cause any synchronization issues if multiple requests are incoming?

Please let me know.

Thank you.


#2

Adding a field to LineCodec is one option, hinted at in the example you’ve linked to:

In general, codecs may need local state, for example to record information about incomplete decoding. We can get away without it, though […]

There should be no synchronization issues: every connection has its own codec instance, and the protocol should make sure that requests on a single connection are properly framed.

For some categories of length-delimited frames, one can also use codec::length_delimited from tokio_io.


#3

Thank you for the response. I think I will just use the header field suggestion, I just wanted to be aware of the other options out there.


#4

Here’s an alternative. https://github.com/tokio-rs/tokio-minihttp/blob/master/src/request.rs

Your codec can try to decode the next packet. If it fails (not enough data), return Ok(None). If it succeeds, create a struct instance with the header and data returned as Ok(Some(StructWithHeaders)).

The code in minihttp decodes the buffer from scratch everytime, there is no partial saving of state to work from when the buffer had too little data.