Just An Application

July 2, 2013

Programming With Rust — Part Eighteen: Let’s Build A Library

We are now in a position to do something with an incoming HTTP Request, but what ?

HTTP is a versatile protocol, but a protocol is all it is. What a GET Request, for example, should actually get and how is up for grabs.

Rather than hardwire it for one implementation of GET, or POST or what have you, what we can do is turn httpd into a ‘framework’ which just deals with the protocol part and leave the semantics to the user of the framework.

The first step is to turn our executable into a library.

1.0 A Brief Introduction To Rust Attributes

Rust Attributes are a form of metadata which can be associated with crates and items.

An Attribute declaration is defined like this

    attributes_decl : ’#’ ’[’ attributes ’]’ [ ';' ] ? ; 

    attributes      : attribute [ ’,’ attributes ]* 
    
    attribute       : ident [ ’=’ literal | ’(’ attributes ’)’ ] ? ;

An Attribute declaration applies to the item it precedes unless the declaration is terminated with a semi-colon (;). In this case it applies to the entity in which it is contained.

Currently it is only possible to specify pre-defined Attributes.

2.0 Crate Files And Attributes

We can use the crate_type attribute to specify the type of the binary crate that should be built.

For example, if we include this crate_type Attribute declaration in a crate file

    #[crate_type = "lib"];

then the binary crate built from the crate file will be a library.

The default is bin which is why none of the earlier versions of httpd.rc needed to specify a crate_type attribute.

The name of the library and its version can be specified using the link attribute, like so

    #[link(name = "httpd", vers = "0.6")];

3.0 Modified Source Files

The only existing file we need to change is the crate file, httpd.rc

We need to add the appropriate crate_type and link attributes and remove the main function definition.

We also need to make public those modules that a user of the framework may need to access.

3.1 httpd.rc

// httpd.rc

// Crate file httpd v0.6

#[crate_type = "lib"];

#[link(name = "httpd", vers = "0.6")];


extern mod std;

pub mod HTTP;
pub mod headers;
pub mod request;
pub mod response;
pub mod server;

mod buffer;
mod writer;


4.0 A New Crate: httpd_main

For testing purposes we need a new executable that will link against our new library and initially at least, just run the server as before.

4.1 httpd_main.rc

// httpd_main.rc


extern mod httpd;

use httpd::server;

static PORT: uint = 3534;

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

fn main()
{	
    server::run(IPV4_LOOPBACK, PORT);
}

5.0 Building The Library

We build the library by compiling the crate file as usual

    rustc httpd.rc

This produces a Mac OS X dynamic library

    libhttpd-94839cbfe144198-0.6.dylib

6.0 Building The Executable

We build the executable by compiling its crate file but in this case we need to tell the compiler where the library is using the -L flag like so

    rustc -L . httpd_main.rc

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 21, 2013

Programming With Rust — Part Fourteen: Crates, Modules And Files

Whilest adding the RequestBuffer definitions directly to the existing httpd.rc file was OK for the purposes of experimentation it is not a good habit to get into.

If we keep lobbing everything into httpd.rc before very long it will have turned into an impenetrable “wall of code”.

We need to place sets of related definitions in separate files and then specify that they are part of the crate.

1.0 Crate Files And Modules

A crate comprises an anonymous module and the items it contains.

These items are defined in the crate file.

The items in the crate file can include module items.

A module item defines a module nested within the anonymous top-level module of the crate.

A module item can define a module and the items it contains explicitly or it can reference a module contained in another file.

A module item which references a module in another file is of the form

    "mod" module_name ';'

By default this identifies the module contained in a file of the same name with the suffix “.rs”.

For example, the module item

    mod buffer;

identifies the module buffer in the file buffer.rs

2.0 Source Files And Modules

A Rust source file implicitly defines a module which contains all the items within that file.

When specified as part of a crate the name of the implicitly defined module is determined by the module item in the crate file which references it.

3.0 Moving Items Into Separate Files

We effectively have two sets of items which we can move into separate files.

  • the items which define the RequestBuffer and implement it, and

  • the function definitions and static items for listening on a socket and accepting a connection

We will move these into the files

    buffer.rs

and

    server.rs

respectively.

3.1 buffer.rs

We need to make the RequestBuffer struct and the static method new and the method readLine public so they can be accessed from outside the module. [1].

3.2 server.rs

In server.rs we need to add

    use buffer::RequestBuffer;

and we are going to rename the main function to run and add parameters for address and port so they can be passed in rather than being hard-wired

httpd.rc

In httpd.rc nearly everything has gone.

We need to add

    ...
    mod buffer;
    mod server;
    ...

We are going to hang on the IPV4_LOOPBACK and PORT static items so we can pass them to the function server::run which we are going to invoke from our new main function.

4.0 The New httpd Crate Module Structure

crate_file

5.0 The Source Code For httpd v0.4

5.1 buffer.rs

// buffer.rs

// part of httpd v0.4


use std::net::tcp::TcpSocketBuf;

// RequestBuffer

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

//

static SIZE: uint = 4096;

// 

static CR: u8 = 13;

static LF: u8 = 10;

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

    pub 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);
        }
    }
}

5.2 server.rs


// server.rs

// part of httpd v0.4

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::socket_buf;

use std::sync::Mutex;

use std::uv_iotask;

use buffer::RequestBuffer;



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

fn new_connection_callback(newConn :TcpNewConnection, chan: SharedChan<Option>)
{
    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 ?");
}

static BACKLOG: uint = 5;


pub fn run(address: &str, port: uint)
{	
    tcp::listen(
        ip::v4::parse_addr(address),
        port,
        BACKLOG,
        &uv_iotask::spawn_iotask(task::task()),
        on_establish_callback,
        new_connection_callback);
}

5.3 httpd.rc

// httpd.rc

// v0.4

extern mod std;

mod buffer;
mod server;

static PORT: uint = 3534;

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

fn main()
{	
    server::run(IPV4_LOOPBACK, PORT);
}

Notes

  • I discovered quite inadvertently that it is not actually necessary to make the RequestBuffer type public explicitly.
    The implementation seems to do this implicitly. This behaviour does not seem to be documented.


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 14, 2013

Programming With Rust — Part Nine: Let’s Build A Program

1.0 Defining A Main Function

We need somewhere to call the listen function from.

For now we will call it directly from the main function of our Rust program,

There are a couple of ways to define one of these.

One way is to define a function called main which takes no arguments and does not return a value, like so


    fn main()
    {	
        tcp::listen(
            ip::v4::parse_addr("127.0.0.1"),
            5,
            3534,
            &uv_iotask::spawn_iotask(task::task()),
            |chan|
            {
                io::println(fmt!("on_establish_callback(%?)", chan));
            },
            |newConn, chan|
            {
                io::println(fmt!("new_connection_callback(%?, %?)", newConn, chan));
                fail!(~"Now what ?");
            });
    }

2.0 Building A Rust Executable

In Rust terminology an executable is a crate.

The Rust compiler takes a single crate in source form and from it produces a single crate in binary form.

The source which defines a crate is contained in a single file, the crate file.

A crate in binary form can be either an executable or a library.

2.1 Linking Against Other Crates

A crate file specifies which other crates containing libraries, if any, the crate being defined should be linked against.

Every crate is automatically linked against the crate containing the core library.

In our case we also need to link against the crate containing the std library.

We specify this dependency like this

    extern mod std;

This is an example of an extern_mod_decl which is a kind of view_item.

2.2 Name Bindings

To make use of the definitions in the externally linked crates we set up some name bindings like so

    ...

    use core::task;

    use std::net::ip;
    use std::net::tcp;

    use std::uv_iotask;
    
    ...

2.3 Compiling The Program

To compile our crate file we simply do this

    rustc httpd.rc

3.0 Running The Program

The result of the compilation is an executable called httpd.

If we just run it we get this

    ./httpd
    on_establish_callback({x: {data: (0x1007098f0 as *())}})

and the program waits for a connection.

If we run it and then use a web browser to connect to 127.0.0.1:3534 we get this (output slightly re-formatted for clarity)

    ./httpd
    on_establish_callback({x: {data: (0x10070a470 as *())}})
    new_connection_callback(NewTcpConn((0x100832c00 as *())), {x: {data: (0x10070a470 as *())}})
    rust: task failed at 'Now what ?', httpd.rc:34
    Assertion failed: (false && "Rust task failed after reentering the Rust stack"), \
        function upcall_call_shim_on_rust_stack, \
        file /Users/simon/Src/lang/rust-0.6/src/rt/rust_upcall.cpp, line 92.
    Abort trap

and the program exits.

4.0 Defining Some Simple Constants

At the moment we have a couple of integer constants sitting in the middle of the code which as everyone knows is bad, bad, bad.

We can fix this by defining them as constants like so

    ...

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

These are both examples of a static_item.

Like other items, static_items are processed at compile time and the values they define are stored in the program’s static memory.

We also have a string literal in the middle of the code.

We can define it as a constant like this

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

In fact the string literal is already a constant stored in static memory.

What we are defining here is a borrowed pointer.

The construct

   'static

is a named lifetime and it tells the compiler how long we want to borrow the pointer for.[1]

5.0 The Source Code For httpd v0.1

    // httpd.rc

    // v 0.1

    extern mod std;

    use core::task;

    use std::net::ip;
    use std::net::tcp;

    use std::uv_iotask;

    static BACKLOG: uint = 5;
    static PORT:    uint = 3534;
    
    static IPV4_LOOPBACK: &'static str = "127.0.0.1";

    fn main()
    {	
        tcp::listen(
            ip::v4::parse_addr(IPV4_LOOPBACK),
            PORT,
            BACKLOG,
            &uv_iotask::spawn_iotask(task::task()),
            |chan|
            {
                io::println(fmt!("on_establish_callback(%?)", chan));
            },
            |newConn, chan|
            {
                io::println(fmt!("new_connection_callback(%?, %?)", newConn, chan));
                fail!(~"Now what ?");
            });
    }

Notes

  1. Given the context I am not sure why the compiler cannot infer the required lifetime, but it doesn’t, so you have to tell it or it gets upset.


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.

Blog at WordPress.com.

%d bloggers like this: