Just An Application

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.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: