Just An Application

June 20, 2013

Programming With Rust — Part Thirteen: Reading An HTTP Request

Now we have a connection we can read the incoming HTTP request.

1.0 An HTTP Request

An HTTP request arrives over a connection as

  • a request line

followed by

  • zero or more header lines

followed by

  • an empty line

followed by

  • a message body

which is optional.

A line is terminated by a carriage-return (CR == ASCII 13) immediately followed by a line-feed (LF == ASCII 10).

Ideally we would like to read the entire request from the connection “in one go” but that is not possible because there is no way of knowing how big a given HTTP request is before we read it.

To abstract out the unfortunately indeterminate nature of an incoming HTTP request, we will start by defining a RequestBuffer type which will deal with the vagaries of reading the necessary bytes from the connection and converting them into lines.

2.0 RequestBuffer: Take One

The original idea was that RequestBuffer would look something like this

    struct RequestBuffer
    {
        priv socketBuf:     TcpSocketBuf,
        priv bytes:         ~[u8],
        priv size:          uint,
        priv available:     uint,
        priv position:      uint,
        priv lastLineEnd:   uint
    }

and there would be a readLine method which would look something like this

    impl RequestBuffer
    {
        ...
        
        fn readLine(&mut self) -> ~str
        {
            let mut state = 0;
		
            loop
            {
                if (self.position == self.available)
                {
                    // read all the bytes currently available from the connection
            
                    ...
                }
            
                let b = self.bytes[self.position];
			
                self.position += 1;
            
                match state
                {
                    0 => 
                    {
                        if (b == 13) // CR
                        {
                            state = 1;
                        }
                    },
						
                    1 => 
                    {
                        if (b == 10) // LF
                        {
                            // make string representing line from buffered bytes
                    
                            let line = ...
                        
                            self.lastLineEnd = self.position;
                            return line;
                        }
                        else
                        {
                            state = 0;
                        }
                    },
						
                    _ => 
                    {
                        fail!(fmt!("state == %u !", state));
                    }
                }
            }
        }
        
        ...
        
    }

but at the moment there does not seem to be any way of simply reading all the bytes currently available on the connection in one go that actually works.

2.1 RequestBuffer: Take Two

This is a version that works but there really isn’t a whole lot of buffering going on.

2.1.1 RequestBuffer

    struct RequestBuffer
    {
        priv socketBuf: TcpSocketBuf,
        priv bytes:     ~[u8],
    }

2.1.2 The readLine Method

    ...

    static CR: u8 = 13;

    static LF: u8 = 10;

    ...

    impl RequestBuffer
    {
    
        ...
        
        fn readLine(&mut self) -> ~str
        {
            self.bytes.clear();
        
            let mut state = 0;
		
            loop
            {
                let i = self.socketBuf.read_byte();
			
                if (i < 0)
                {
                    fail!(~"EOF");
                }
            
                let b = i as u8;
            
                match state
                {
                    0 => 
                    {
                        if (b == CR)
                        {
                            state = 1;
                        }
                    },
						
                    1 => 
                    {
                        if (b == LF)
                        {
                            return str::from_bytes(vec::const_slice(self.bytes, 0, self.bytes.len() - 1));
                        }
                        else
                        {
                            state = 0;
                        }
                    },
						
                    _ => 
                    {
                        fail!(fmt!("state == %u !", state));
                    }
                }
                self.bytes.push(b);
            }
        }

        ...
        
    }

2.1.3 The new Method

The new method is a static method which can be used to create a RequestBuffer.

    fn new(socketBuf: TcpSocketBuf) -> RequestBuffer
    {
        RequestBuffer { socketBuf: socketBuf, bytes: ~[0u8, ..SIZE] }
    }

3.0 The handleConnection Function

If the accept function is successful we now call the handleConnection function which is defined like this

    fn handleConnection(socket: TcpSocket)
    {	
        let mut buffer      = RequestBuffer::new(socket_buf(socket));
        let     requestLine = buffer.readLine();
	
        io::println(requestLine);
        loop
        {
            let line = buffer.readLine();
		
            io::println(line);
            if (str::len(line) == 0)
            {
                break;
            }
        }
        io::stdout().flush();
        fail!(~"Now what ?");
    }

4.0 Running The Code

Running the code and pointing a web browser at 127.0.0.1:3534 produces this

 
    ./httpd
    on_establish_callback({x: {data: (0x1007094f0 as *())}})
    new_connection_callback(NewTcpConn((0x10200b000 as *())), {x: {data: (0x1007094f0 as *())}})
    accept succeeded
    GET / HTTP/1.1
    Host: 127.0.0.1:3534
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:21.0) Gecko/20100101 Firefox/21.0
    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
    DNT: 1
    Connection: keep-alive

    rust: task failed at 'Now what ?', httpd.rc:172
    rust: domain main @0x10201ee10 root task failed
    rust: task failed at 'killed', /Users/simon/Src/lang/rust-0.6/src/libcore/pipes.rs:314

5.0 The Source Code For httpd v0.3


// httpd.rc

// v0.3

extern mod std;

use core::comm::SharedChan;

use core::option::Option;


use core::task;

use std::net::ip;
use std::net::tcp;
use std::net::tcp::TcpErrData;
use std::net::tcp::TcpNewConnection;
use std::net::tcp::TcpSocket;
use std::net::tcp::TcpSocketBuf;

use std::net::tcp::socket_buf;

use std::sync::Mutex;

use std::uv_iotask;

// RequestBuffer

struct RequestBuffer
{
    priv socketBuf: TcpSocketBuf,
    priv bytes:     ~[u8],
}

//

static SIZE: uint = 4096;

// 

static CR: u8 = 13;

static LF: u8 = 10;

impl RequestBuffer
{    
    fn new(socketBuf: TcpSocketBuf) -> RequestBuffer
    {
        RequestBuffer { socketBuf: socketBuf, bytes: ~[0u8, ..SIZE] }
    }

    fn readLine(&mut self) -> ~str
    {
        self.bytes.clear();
        
        let mut state = 0;
		
        loop
        {
            let i = self.socketBuf.read_byte();
			
            if (i < 0)
            {
                fail!(~"EOF");
            }
            
            let b = i as u8;
            
            match state
            {
                0 => 
                {
                    if (b == CR)
                    {
                        state = 1;
                    }
                },
						
                1 => 
                {
                    if (b == LF)
                    {
                        return str::from_bytes(vec::const_slice(self.bytes, 0, self.bytes.len() - 1));
                    }
                    else
                    {
                        state = 0;
                    }
                },
						
                _ => 
                {
                    fail!(fmt!("state == %u !", state));
                }
            }
            self.bytes.push(b);
        }
    }
}


static BACKLOG: uint = 5;
static PORT:    uint = 3534;

static IPV4_LOOPBACK: &'static str = "127.0.0.1";


fn on_establish_callback(chan: SharedChan<Option<TcpErrData>>)
{
    io::println(fmt!("on_establish_callback(%?)", chan));
}

fn new_connection_callback(newConn :TcpNewConnection, chan: SharedChan<Option<TcpErrData>>)
{
    io::println(fmt!("new_connection_callback(%?, %?)", newConn, chan));
	
    let mx = Mutex();
	
    do mx.lock_cond
        |cv|
        {
            let mxc = ~mx.clone();

            do task::spawn 
            {
                match tcp::accept(newConn)
                {
                    Ok(socket) => 
                    {
                        io::println("accept succeeded");
                        do mxc.lock_cond
                            |cv|
                            {
                                cv.signal();
                            }
                        handleConnection(socket);
				                
                    },
                    Err(error) => 
                    {
                        io::println(fmt!("accept failed: %?", error));
                        do mxc.lock_cond
                            |cv|
                            {
                                cv.signal();
                            }
                    }
                }
            }
            cv.wait();
        }
}

fn handleConnection(socket: TcpSocket)
{	
    let mut buffer      = RequestBuffer::new(socket_buf(socket));
    let     requestLine = buffer.readLine();
	
    io::println(requestLine);
    loop
    {
        let line = buffer.readLine();
		
        io::println(line);
        if (str::len(line) == 0)
        {
            break;
        }
    }
    io::stdout().flush();
    fail!(~"Now what ?");
}

fn main()
{	
    tcp::listen(
        ip::v4::parse_addr(IPV4_LOOPBACK),
        PORT,
        BACKLOG,
        &uv_iotask::spawn_iotask(task::task()),
        on_establish_callback,
        new_connection_callback);
}


Copyright (c) 2013 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

June 17, 2013

Programming With Rust — Part Eleven: Even More Things We Need To Know – The Portmanteau Edition

1.0 Machine Independent Integers

Rust supports explicitly sized, and hence machine independent, signed and unsigned integer types.

The supported signed integer types are

  • i8

  • i16

  • i32

  • i64

The supported unsigned integer types are, unsurprisingly

  • u8

  • u16

  • u32

  • u64

2.0 Vectors

A Rust vector type specifies a one-dimensional array of values of the same type.

The vector type can specify an exact size

   [u8, ..512]

in which case it can be allocated on the stack.

If the type does not specify an exact size then it cannot be allocated on the stack and the type declaration must identify where it should be allocated, which is either in the managed heap of the current task, like so

   @[u8]

or in the owned heap, like so

   ~[u8]

A value of a vector type can be specified by explicitly enumerating all of its elements

    let o_vec: ~[u8] = ~[0, 1, 2, 3];

or by specifying an initial value and a size.

   let s_vec: [u8, ..512] = [0u8, ..512];

Individual elements of a vector can be accessed by indexing as you might expect.

For example

   let element_zero: u8 = s_vec[0];

3.0 Structs

A Rust struct type defines a set of named values.

For example

    struct QName
    {
        prefix:    ~str,
        nsURI:     ~str,
        localPart: ~str
    }

This is an example of a struct item.

3.1 Creating Values

A value of a struct type is created using a struct expression which has a similar syntax to a struct item

    typename '{' [fieldname ':' expression] [',' fieldname ':' expression ]* '}'

For example you can create an value of type QName like this

    let rights = QName {prefix:~"dc", nsURI:~"http://purl.org/dc/elements/1.1/", localPart:~"rights"};

This creates an immutable value of type QName in the current stack frame.

You can also create a value of a struct type in the managed heap, for example

    let m_rights = @QName {prefix:~"dc", nsURI:~"http://purl.org/dc/elements/1.1/", localPart:~"rights"};

or in the owned heap

    let o_rights = ~QName {prefix:~"dc", nsURI:~"http://purl.org/dc/elements/1.1/", localPart:~"rights"};

3.2 Direct Field Access

You can access the fields of a value of struct type using dot (.) notation, like so

    ...

    let rights_prefix:    &str = rights.prefix;
    let rights_nsURI:     &str = rights.nsURI;
    let rights_localPart: &str = rights.localPart;

    ...

3.3 Access Control

The fields of a value of a struct type are public by default but they can be made private like so

    struct QName
    {
        priv prefix:    ~str,
        priv nsURI:     ~str,
        priv localPart: ~str
    }

The effect of this is to limit construction of values of the struct type, and direct field access, to code defined in the module in which the struct is defined.

4.0 Tuples

A Tuple is an immutable fixed-size collection of values.

A tuple value is written as a parentheses delimited list of comma separated values.

A tuple type is written as a parentheses delimited list of comma separated types.

For example, extending the field access example above, we can do this

    ...

    let rights_prefix:    &str = rights.prefix;
    let rights_nsURI:     &str = rights.nsURI;
    let rights_localPart: &str = rights.localPart;
    
    ...
    
    let rights_tuple:  (&str, &str, &str) = (rights_prefix, rights_nsURI, rights_localPart);

    ...

The only way to access the elements of a tuple is by pattern matching.

For example

   ...

    let (prefix, nsURI, localPart) = rights_tuple;
    
    io::println(fmt!("prefix     == %?", prefix));
    io::println(fmt!("nsURI      == %?", nsURI));
    io::println(fmt!("localPart  == %?", localPart));

    ...

prints

    prefix     == "dc"
    nsURI      == "http://purl.org/dc/elements/1.1/"
    localPart  == "rights"

You can achieve the same effect by using a match expression like this

   match rights_tuple
    {
        (prefix, nsURI, localPart) =>
        {
            io::println(fmt!("prefix     == %?", prefix));
            io::println(fmt!("nsURI      == %?", nsURI));
            io::println(fmt!("localPart  == %?", localPart));
        }
    }

5.0 Argument Passing, Value Returning, And Other Things Of That Ilk

Arguments are passed by value using a shallow copy, so if the argument is a composite value containing pointers then it is only a copy
of the value itself, any pointers it contains point to the same things as the original.

The same is true for return values and for assignment.

As we have already seen, if the value being passed, returned or assigned is an owned pointer then the copy becomes a move as the original is invalidated.

The use of a shallow copy for composite values means that the same is necessarily true if those values directly or indirectly contain one or more owned pointers.

For example, if we have a function defined like this

    fn start_element(name: QName)
    {
        ...
    }

and we invoke it with a stack allocated QName value as its argument like this

    let rights = QName { prefix:~"dc", nsURI:~"http://purl.org/dc/elements/1.1/", localPart:~"rights"};

    start_element(rights);
    
    ...

then after the call to start_element the local variable rights is invalid.

This is because a QName value contains three owned pointers, so when it is passed to the function using a shallow copy the ownership of those owned pointers must be transferred, so the original value must be invalidated as it is no longer usable.


Copyright (c) 2013 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Create a free website or blog at WordPress.com.

%d bloggers like this: