1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
extern crate bufstream;
use bufstream::BufStream;
use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};
use std::io;
use std::io::{Read, Write};
#[cfg(test)] use std::net::{TcpStream, TcpListener};
use std::str::{from_utf8, Utf8Error};
use ScgiError::*;
#[derive(Debug)]
pub enum ScgiError {
BadLength,
Utf8 (Utf8Error),
WrongLength (String),
WrongHeaders,
EOF,
IO (io::Error)
}
impl From<io::Error> for ScgiError {fn from (io_error: io::Error) -> ScgiError {IO (io_error)}}
impl From<Utf8Error> for ScgiError {fn from (utf8_error: Utf8Error) -> ScgiError {Utf8 (utf8_error)}}
impl Display for ScgiError {
fn fmt (&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> {write! (fmt, "{:?}", self)}
}
impl std::error::Error for ScgiError {
fn description(&self) -> &str {
"ScgiError"
}
fn cause(&self) -> Option<&std::error::Error> {
match *self {
Utf8(ref e) => Some(e),
IO(ref e) => Some(e),
_ => None,
}
}
}
pub fn read_headers<S: Read + Write> (stream: S) -> Result<(Vec<u8>, BufStream<S>), ScgiError> {
let mut stream = BufStream::new (stream);
let raw_headers: Vec<u8>;
let mut length_string: [u8; 10] = unsafe {std::mem::uninitialized()};
let mut length_string_len = 0usize;
loop {
let mut ch_buf = [0u8];
let got = try! (stream.read (&mut ch_buf));
if got == 0 {return Err (EOF)}
let ch = ch_buf[0];
if ch >= b'0' && ch <= b'9' {
length_string[length_string_len] = ch; length_string_len += 1;
} else if ch == b':' {
let length_str = try! (from_utf8 (&length_string[0 .. length_string_len]));
let length: usize = try! (length_str.parse().map_err (|_| BadLength));
let mut headers_buf = Vec::with_capacity (length);
unsafe {headers_buf.set_len (length)}
let mut total = 0;
while total < length {
let got = try! (stream.read (&mut headers_buf[total .. length]));
if got == 0 {return Err (EOF)}
total += got
}
if try! (stream.read (&mut ch_buf)) != 1 || ch_buf[0] != b',' {return Err (WrongLength (length_str.to_string()))}
raw_headers = headers_buf; break;
} else {
length_string[length_string_len] = ch; length_string_len += 1;
return Err (WrongLength (try! (from_utf8 (&length_string[0 .. length_string_len])).to_string()));
}
};
Ok ((raw_headers, stream))
}
pub fn parse<'h,H> (raw_headers: &'h [u8], mut header: H) -> Result<(), ScgiError> where H: FnMut(&'h str,&'h str) {
let mut pos = 0usize;
while pos < raw_headers.len() {
let zero1 = try! (raw_headers[pos..].iter().position (|&ch|ch == 0) .ok_or (WrongHeaders));
let header_name = try! (from_utf8 (&raw_headers[pos .. pos + zero1]));
pos = pos + zero1 + 1;
let zero2 = try! (raw_headers[pos..].iter().position (|&ch|ch == 0) .ok_or (WrongHeaders));
let header_value = try! (from_utf8 (&raw_headers[pos .. pos + zero2]));
header (header_name, header_value);
pos = pos + zero2 + 1;
}
Ok(())
}
pub fn string_map (raw_headers: &[u8]) -> Result<BTreeMap<String, String>, ScgiError> {
let mut headers_map = BTreeMap::new();
try! (parse (raw_headers, |name,value| {headers_map.insert (name.to_string(), value.to_string());}));
Ok (headers_map)
}
pub fn str_map<'h> (raw_headers: &'h [u8]) -> Result<BTreeMap<&'h str, &'h str>, ScgiError> {
let mut headers_map = BTreeMap::new();
try! (parse (raw_headers, |name,value| {headers_map.insert (name, value);}));
Ok (headers_map)
}
#[test] fn test_scgi() {
let port = 13123;
std::thread::spawn (move|| {
std::thread::sleep_ms (10);
let mut stream = TcpStream::connect (("127.0.0.1", port)) .unwrap();
stream.write (b"70:CONTENT_LENGTH\x0056\x00SCGI\x001\x00REQUEST_METHOD\x00POST\x00REQUEST_URI\x00/deepthought\x00,") .unwrap();
stream.write (b"What is the answer to life, the Universe and everything?") .unwrap();
let mut buf = String::new();
stream.read_to_string (&mut buf).unwrap();
assert_eq! (&buf[..], "Status: 200 OK\r\nContent-Type: text/plain\r\n\r\n42");
});
let acceptor = TcpListener::bind (("127.0.0.1", port)) .unwrap();
let stream = acceptor.incoming().next().unwrap();
match stream {
Err (err) => {panic! ("Accept error: {}", err)},
Ok (tcp_stream) => {
let (raw_headers, mut stream) = read_headers (tcp_stream) .unwrap();
assert_eq! (str_map (&raw_headers) .unwrap() ["REQUEST_URI"], "/deepthought");
assert_eq! (&(string_map (&raw_headers) .unwrap() ["REQUEST_URI"])[..], "/deepthought");
stream.write_all (b"Status: 200 OK\r\nContent-Type: text/plain\r\n\r\n42") .unwrap();
}
}
}