Implementing HTTP

Jonathan Reem

@jreem

github.com/reem

What does it look like?


    GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1
    Host: net.tutsplus.com
    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 300
    Connection: keep-alive
    Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120
    Pragma: no-cache
    Cache-Control: no-cache

Major Parts

  • Methods
  • Headers
  • Status
  • Body

Methods

  • Get, Post, etc.

Use Strings


    pub type Method = String;

    pub static Get: Method = "GET";
    pub static Post: Method = "POST";
    // etc.

    request.method = Get;

Use Numbers


    pub type Method = u16;

    pub static Get: Method = 1u16;
    pub static Post: Method = 2u16;
    // etc.

    request.method = Get;

Use a Struct


    pub struct Method { 
        name: String, 
        idempotent: bool, 
        safe: bool
    }

    pub static Get: Method = Method { 
        name: "GET", 
        idempotent: true, 
        safe: true 
    };

    pub static Post: Method = Method {
        name: "POST",
        idempotent: false,
        safe: false
    };
    // etc.

    request.method = Get;

Use an `enum`


    pub enum Method {
        Get, 
        Post, // etc.
    }


    request.method = Get;

Headers

  • Content-Length, Transfer-Encoding, Content-Type, etc.

Use Strings


    pub struct Headers {
        data: HashMap<String, String>
    }

    pub static TransferEncoding: String = "transfer-encoding";
    pub static ContentLength: String = "content-length";
    // etc.

    request.headers.set(TransferEncoding, "Gzip");

Use Traits!


    pub struct Headers {
        data: HashMap<String, HeaderItem>
    }

    pub enum HeaderItem {
        Raw(String),
        Typed(Box<Header>)
    }

    pub trait Header {
        fn name() -> String;
        fn parse(String) -> Self;
        fn display(self) -> String;
    }

    pub enum TransferEncoding { Gzip, Compress, Deflate, Chunked }

    impl Header for TransferEncoding {
        fn name() -> String { "transfer-encoding" }
        fn parse(raw: String) -> TransferEncoding {
            match raw {
                "gzip" => Gzip, // etc.
            }
        }
        fn display(self) -> String { self.to_string() }
    }

    request.headers.set(Gzip);

Status

  • 200 OK, 404 NOT FOUND, 500 INTERNAL SERVER ERROR, etc.

Use an enum


    pub enum Status {
       Continue = 100,
       SwitchingProtocols = 101,
       Processing = 102,
       Code103 = 103,
       // etc.
    }

   response.status = Continue;

Body

  • To stream or not to stream
  • Track statically vs. track dynamically.

Stream, dynamically


    pub struct Response {
        body: TcpStream,
        headers: Headers,
        headers_written: bool
    }

    impl Writer for Response {
       fn write(&self, msg: Bytes) {
           if !self.headers_written {
               self.write_headers();
               self.headers_written = true;
           }
           self.body.write(msg);
       }
    }

    response.headers.set(Gzip);
    response.write(b"Hello World!");

    // Woops
    response.headers.set(Compress);

Stream, statically!


    pub struct Fresh; pub struct Streaming;
    pub struct Response<WriteStatus> { body: TcpStream, headers: Headers }

    impl<W> Response<W> {
       fn headers(&self) -> &Headers { &self.headers }
    }

    impl Response<Fresh> {
       fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
       fn start(self) -> Response<Streaming> {
          self.write_headers();
          Response { body: self.body, headers: self.headers }
       }
    }

    impl Writer for Response<Streaming> {
       fn write(&self, msg: Bytes) {
           self.body.write(msg);
       }
    }

    response.headers_mut().set(Gzip);
    let response = response.start();
    response.write(b"Hello World!");

    // Compile time error.
    response.headers_mut().set(Compress);

Congrats!

Further Reading

  • Http Parsing
  • Buffered reads/writes
  • Tcp
  • Header Interoperation
  • Header Invalidation
  • Errors
  • Handlers
  • Status Code Classes
  • Decoding

Implementing HTTP

By Jonathan Reem

Implementing HTTP

  • 1,685