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
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.