├── .gitignore ├── Cargo.toml ├── LICENSE.txt ├── README.md ├── examples └── hello.rs └── src ├── lib.rs ├── unix.rs └── windows.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fastcgi" 3 | version = "1.0.0" 4 | authors = ["Mohd Tarmizi Mohd Affandi"] 5 | license = "MIT" 6 | description = "FastCGI library written in pure Rust" 7 | documentation = "https://docs.rs/fastcgi" 8 | repository = "https://github.com/mohtar/rust-fastcgi" 9 | readme = "README.md" 10 | keywords = ["fastcgi", "web", "server", "protocol", "cgi"] 11 | 12 | [dependencies] 13 | libc = "0.2.9" 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mohd Tarmizi Mohd Affandi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FastCGI library for Rust 2 | ======================== 3 | 4 | Copyright (c) 2015 Mohd Tarmizi Mohd Affandi 5 | 6 | Licensed under the MIT License. See `LICENSE.txt` 7 | 8 | Documentation 9 | ------------- 10 | 11 | Read the generated API documentation: 12 | https://docs.rs/fastcgi 13 | -------------------------------------------------------------------------------- /examples/hello.rs: -------------------------------------------------------------------------------- 1 | extern crate fastcgi; 2 | 3 | use std::io::Write; 4 | 5 | fn main() { 6 | fastcgi::run(|mut req| { 7 | write!(&mut req.stdout(), "Content-Type: text/plain\n\nHello, world!") 8 | .unwrap_or(()); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // // 3 | // Copyright (c) 2015 Mohd Tarmizi Mohd Affandi // 4 | // // 5 | // Permission is hereby granted, free of charge, to any person obtaining a // 6 | // copy of this software and associated documentation files (the // 7 | // "Software"), to deal in the Software without restriction, including // 8 | // without limitation the rights to use, copy, modify, merge, publish, // 9 | // distribute, sublicense, and/or sell copies of the Software, and to // 10 | // permit persons to whom the Software is furnished to do so, subject to // 11 | // the following conditions: // 12 | // // 13 | // The above copyright notice and this permission notice shall be included // 14 | // in all copies or substantial portions of the Software. // 15 | // // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // 19 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // 20 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // 21 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // 22 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 23 | // // 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | //! Pure Rust implementation of FastCGI 1.0. 27 | //! 28 | //! Example usage: 29 | //! 30 | //! extern crate fastcgi; 31 | //! 32 | //! use std::io::Write; 33 | //! 34 | //! fn main() { 35 | //! if !fastcgi::is_fastcgi() { return } 36 | //! fastcgi::run(|mut req| { 37 | //! write!(&mut req.stdout(), "Content-Type: text/plain\n\nHello, world!") 38 | //! .unwrap_or(()); 39 | //! }); 40 | //! } 41 | 42 | extern crate libc; 43 | 44 | use std::collections::{HashMap, HashSet}; 45 | use std::io::{self, Read, Write, Cursor, BufRead}; 46 | use std::marker::{Send, Sync}; 47 | use std::mem; 48 | use std::net::TcpListener; 49 | use std::rc::Rc; 50 | use std::sync::Arc; 51 | use std::thread; 52 | 53 | #[cfg(unix)] use unix::{Transport, Socket}; 54 | #[cfg(unix)] mod unix; 55 | 56 | #[cfg(windows)] use windows::{Transport, Socket}; 57 | #[cfg(windows)] mod windows; 58 | 59 | const HEADER_LEN: usize = 8; 60 | 61 | #[derive(Debug, Clone, Copy)] 62 | pub enum Role { 63 | Responder, 64 | Authorizer, 65 | Filter, 66 | } 67 | 68 | #[derive(Debug)] 69 | enum ProtocolStatus { 70 | RequestComplete, 71 | CantMpxConn, 72 | #[allow(dead_code)] Overloaded, 73 | UnknownRole, 74 | } 75 | 76 | #[derive(Debug)] 77 | enum Record { 78 | BeginRequest { request_id: u16, role: Result, keep_conn: bool }, 79 | AbortRequest { request_id: u16 }, 80 | EndRequest { request_id: u16, app_status: i32, protocol_status: ProtocolStatus }, 81 | Params { request_id: u16, content: Vec }, 82 | Stdin { request_id: u16, content: Vec }, 83 | Stdout { request_id: u16, content: Vec }, 84 | Stderr { request_id: u16, content: Vec }, 85 | Data { request_id: u16, content: Vec }, 86 | GetValues(Vec), 87 | GetValuesResult(Vec<(String, String)>), 88 | UnknownType(u8), 89 | } 90 | 91 | fn read_len(mut r: &mut R) -> io::Result { 92 | let mut buf: Vec = Vec::with_capacity(4); 93 | (&mut r).take(1).read_to_end(&mut buf)?; 94 | if buf.len() == 1 { 95 | if buf[0] >> 7 == 1 { 96 | assert!((&mut r).take(3).read_to_end(&mut buf)? == 3); 97 | Ok( 98 | (((buf[0] & 0x7f) as u32) << 24) 99 | + ((buf[1] as u32) << 16) 100 | + ((buf[2] as u32) << 8) 101 | + (buf[3] as u32) 102 | ) 103 | } else { 104 | Ok(buf[0] as u32) 105 | } 106 | } else { 107 | Err(io::Error::new(io::ErrorKind::Other, "EOF")) 108 | } 109 | } 110 | 111 | fn read_pair(mut r: &mut R) -> io::Result<(String, String)> { 112 | let key_len = read_len(r)?; 113 | let value_len = read_len(r)?; 114 | let mut key = String::with_capacity(key_len as usize); 115 | assert!((&mut r).take(key_len as u64).read_to_string(&mut key)? == key_len as usize); 116 | let mut value = String::with_capacity(value_len as usize); 117 | assert!((&mut r).take(value_len as u64).read_to_string(&mut value)? == value_len as usize); 118 | Ok((key, value)) 119 | } 120 | 121 | fn read_pairs(r: &mut R) -> io::Result> { 122 | let mut params = Vec::new(); 123 | match read_pair(r) { 124 | Ok(param) => { 125 | params.push(param); 126 | params.extend(read_pairs(r)?.into_iter()); 127 | Ok(params) 128 | }, 129 | Err(_) => Ok(params), 130 | } 131 | } 132 | 133 | fn write_len(w: &mut W, n: u32) -> io::Result<()> { 134 | if n < 0x80 { 135 | w.write_all(&[n as u8])?; 136 | } else { 137 | assert!(n < 0x80000000); 138 | let buf = unsafe { 139 | mem::transmute::((0x80000000 & n).to_be()) 140 | }; 141 | w.write_all(&buf)?; 142 | } 143 | Ok(()) 144 | } 145 | 146 | fn write_pairs(w: &mut W, pairs: Vec<(String, String)>) -> io::Result<()> { 147 | for (key, value) in pairs { 148 | write_len(w, key.len() as u32)?; 149 | write_len(w, value.len() as u32)?; 150 | write!(w, "{}{}", key, value)?; 151 | } 152 | Ok(()) 153 | } 154 | 155 | #[inline] 156 | fn write_record(w: &mut W, record_type: u8, request_id: u16, content: &[u8]) -> io::Result<()> { 157 | assert!(content.len() <= std::u32::MAX as usize); 158 | let request_id = unsafe { 159 | mem::transmute::<_, [u8; 2]>(request_id.to_be()) 160 | }; 161 | let content_length = unsafe { 162 | mem::transmute::<_, [u8; 2]>((content.len() as u16).to_be()) 163 | }; 164 | w.write_all(&[ 165 | 1, record_type, request_id[0], request_id[1], 166 | content_length[0], content_length[1], 0, 0, 167 | ])?; // TODO: Padding 168 | w.write_all(content)?; 169 | Ok(()) 170 | } 171 | 172 | #[inline] 173 | fn read_record(r: &mut R) -> io::Result<(u8, u16, Vec)> { 174 | let mut header: Vec = Vec::with_capacity(HEADER_LEN); 175 | assert!(r.take(HEADER_LEN as u64).read_to_end(&mut header)? == HEADER_LEN); 176 | assert!(header[0] == 1); 177 | let record_type = header[1]; 178 | let request_id = unsafe { u16::from_be(mem::transmute([header[2], header[3]])) }; 179 | let content_length = unsafe { u16::from_be(mem::transmute([header[4], header[5]])) }; 180 | let padding_length = header[6]; 181 | let mut content: Vec = Vec::with_capacity(content_length as usize); 182 | assert!(r.take(content_length as u64).read_to_end(&mut content)? == content_length as usize); 183 | assert!(r.take(padding_length as u64).read_to_end(&mut Vec::with_capacity(padding_length as usize))? == padding_length as usize); 184 | Ok((record_type, request_id, content)) 185 | } 186 | 187 | impl Record { 188 | fn send(self, w: &mut W) -> io::Result<()> { 189 | match self { 190 | Record::EndRequest { request_id, app_status, protocol_status } => { 191 | let app_status = unsafe { 192 | mem::transmute::<_, [u8; 4]>(app_status.to_be()) 193 | }; 194 | let protocol_status = match protocol_status { 195 | ProtocolStatus::RequestComplete => 0, 196 | ProtocolStatus::CantMpxConn => 1, 197 | ProtocolStatus::Overloaded => 2, 198 | ProtocolStatus::UnknownRole => 3, 199 | }; 200 | let content = [ 201 | app_status[0], app_status[1], app_status[2], app_status[3], 202 | protocol_status, 0, 0, 0, 203 | ]; 204 | write_record(w, 3, request_id, &content)?; 205 | }, 206 | Record::Stdout { request_id, content } => { 207 | write_record(w, 6, request_id, &content)?; 208 | }, 209 | Record::Stderr { request_id, content } => { 210 | write_record(w, 7, request_id, &content)?; 211 | }, 212 | Record::GetValuesResult(items) => { 213 | let mut content = Cursor::new(Vec::new()); 214 | write_pairs(&mut content, items)?; 215 | write_record(w, 10, 0, &content.into_inner())?; 216 | }, 217 | Record::UnknownType(record_type) => { 218 | let content = [record_type, 0, 0, 0, 0, 0, 0, 0]; 219 | write_record(w, 11, 0, &content)?; 220 | }, 221 | _ => panic!("Record not sendable"), 222 | } 223 | Ok(()) 224 | } 225 | 226 | fn receive(r: &mut R) -> io::Result { 227 | let (record_type, request_id, content) = read_record(r)?; 228 | let rec = match record_type { 229 | 1 => { 230 | let role = unsafe { 231 | u16::from_be(mem::transmute([content[0], content[1]])) 232 | }; 233 | let role = match role { 234 | 1 => Ok(Role::Responder), 235 | 2 => Ok(Role::Authorizer), 236 | 3 => Ok(Role::Filter), 237 | _ => Err(role), 238 | }; 239 | let keep_conn = content[2] & 1 == 1; 240 | Record::BeginRequest { 241 | request_id: request_id, 242 | role: role, 243 | keep_conn: keep_conn 244 | } 245 | }, 246 | 2 => Record::AbortRequest { request_id: request_id }, 247 | 4 => Record::Params { request_id: request_id, content: content }, 248 | 5 => Record::Stdin { request_id: request_id, content: content }, 249 | 8 => Record::Data { request_id: request_id, content: content }, 250 | 9 => { 251 | let items = read_pairs(&mut Cursor::new(content))?; 252 | Record::GetValues(items.into_iter().map(|(key, _)| key).collect()) 253 | }, 254 | _ if record_type >= 11 => Record::UnknownType(record_type), 255 | _ => panic!("Record not receivable"), 256 | }; 257 | Ok(rec) 258 | } 259 | } 260 | 261 | pub struct Stdin<'a> { 262 | req: &'a mut Request, 263 | } 264 | 265 | impl<'a> Stdin<'a> { 266 | /// Begin reading the second stream of the request, for FastCGI Filter 267 | /// applications. 268 | /// 269 | /// May only be called after all contents of stdin has been read. Panics 270 | /// if stdin has not reached EOF yet. 271 | pub fn start_filter_data(&mut self) { 272 | if !self.req.filter_data { 273 | assert!(self.req.is_eof); 274 | self.req.is_eof = false; 275 | self.req.filter_data = true; 276 | } 277 | } 278 | } 279 | 280 | impl<'a> BufRead for Stdin<'a> { 281 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 282 | if self.req.aborted { 283 | return Err(io::Error::new(io::ErrorKind::ConnectionAborted, "Request aborted")); 284 | } 285 | if self.req.pos == self.req.buf.len() && !self.req.is_eof { 286 | let mut sock = &*self.req.sock; 287 | loop { 288 | match (Record::receive(&mut sock)?, self.req.filter_data) { 289 | (Record::UnknownType(rec_type), _) => { 290 | Record::UnknownType(rec_type).send(&mut sock)?; 291 | }, 292 | (Record::GetValues(keys), _) => { 293 | Record::GetValuesResult(get_values(keys)) 294 | .send(&mut sock)? 295 | }, 296 | (Record::BeginRequest { request_id, .. }, _) => { 297 | Record::EndRequest { 298 | request_id: request_id, 299 | app_status: 0, 300 | protocol_status: ProtocolStatus::CantMpxConn, 301 | } 302 | .send(&mut sock)? 303 | }, 304 | (Record::AbortRequest { request_id }, _) => { 305 | if request_id != self.req.id { 306 | continue; 307 | } 308 | self.req.aborted = true; 309 | return Err(io::Error::new(io::ErrorKind::ConnectionAborted, "Request aborted")); 310 | } 311 | (Record::Stdin { request_id, content }, false) 312 | | (Record::Data { request_id, content }, true) => { 313 | if request_id != self.req.id { 314 | continue; 315 | } 316 | if content.is_empty() { 317 | self.req.is_eof = true; 318 | } 319 | self.req.buf = content; 320 | self.req.pos = 0; 321 | break; 322 | }, 323 | _ => (), 324 | } 325 | } 326 | } 327 | Ok(&self.req.buf[self.req.pos..]) 328 | } 329 | 330 | fn consume(&mut self, amount: usize) { 331 | self.req.pos = std::cmp::min(self.req.pos + amount, self.req.buf.len()); 332 | } 333 | } 334 | 335 | impl<'a> Read for Stdin<'a> { 336 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 337 | let n = { 338 | let mut chunk = self.fill_buf()?; 339 | chunk.read(buf)? 340 | }; 341 | self.consume(n); 342 | Ok(n) 343 | } 344 | } 345 | 346 | macro_rules! writer { 347 | ($Writer:ident) => ( 348 | pub struct $Writer<'a> { 349 | req: &'a mut Request, 350 | } 351 | 352 | impl<'a> Write for $Writer<'a> { 353 | fn write(&mut self, buf: &[u8]) -> io::Result { 354 | if self.req.aborted { 355 | return Err(io::Error::new(io::ErrorKind::ConnectionAborted, "Request aborted")); 356 | } 357 | if buf.is_empty() { 358 | Ok(0) 359 | } else { 360 | for chunk in buf.chunks(std::u16::MAX as usize) { 361 | let rec = Record::$Writer { 362 | request_id: self.req.id, 363 | content: chunk.to_owned(), 364 | }; 365 | rec.send(&mut &*self.req.sock)?; 366 | } 367 | Ok(buf.len()) 368 | } 369 | } 370 | 371 | fn flush(&mut self) -> io::Result<()> { 372 | Ok(()) 373 | } 374 | } 375 | ); 376 | } 377 | 378 | writer!(Stdout); 379 | 380 | writer!(Stderr); 381 | 382 | /// Request objects are what a FastCGI application will primarily deal with 383 | /// throughout its lifetime. 384 | /// 385 | /// The Request API is designed to be an abstraction of the traditional CGI 386 | /// process model. Note that this API is low level. Dealing with things like 387 | /// GET/POST parameters or cookies is outside the scope of this library. 388 | pub struct Request { 389 | sock: Rc, 390 | id: u16, 391 | role: Role, 392 | params: HashMap, 393 | aborted: bool, 394 | status: i32, 395 | buf: Vec, 396 | pos: usize, 397 | is_eof: bool, 398 | filter_data: bool, 399 | } 400 | 401 | pub type Params<'a> = Box + 'a>; 402 | 403 | fn get_values(keys: Vec) -> Vec<(String, String)> { 404 | keys.into_iter().filter_map(|key| 405 | match key.as_ref() { 406 | "FCGI_MAX_CONNS" => Some((key, "1".to_owned())), 407 | "FCGI_MAX_REQS" => Some((key, "1".to_owned())), 408 | "FCGI_MPXS_CONNS" => Some((key, "0".to_owned())), 409 | _ => None, 410 | } 411 | ).collect() 412 | } 413 | 414 | impl Request { 415 | fn begin(mut sock: &Socket) -> io::Result<(u16, Role, bool)> { 416 | loop { 417 | match Record::receive(&mut sock)? { 418 | Record::UnknownType(rec_type) => { 419 | Record::UnknownType(rec_type).send(&mut sock)?; 420 | }, 421 | Record::GetValues(keys) => { 422 | Record::GetValuesResult(get_values(keys)).send(&mut sock)?; 423 | }, 424 | Record::BeginRequest { request_id, role: Ok(role), keep_conn } => { 425 | return Ok((request_id, role, keep_conn)); 426 | }, 427 | Record::BeginRequest { request_id, role: Err(_), .. } => { 428 | Record::EndRequest { 429 | request_id: request_id, 430 | app_status: 0, 431 | protocol_status: ProtocolStatus::UnknownRole 432 | }.send(&mut sock)?; 433 | }, 434 | _ => (), 435 | } 436 | } 437 | } 438 | 439 | fn new(sock: Rc, id: u16, role: Role) -> io::Result { 440 | let mut buf = Vec::new(); 441 | let mut params = HashMap::new(); 442 | let mut aborted = false; 443 | loop { 444 | match Record::receive(&mut &*sock)? { 445 | Record::UnknownType(rec_type) => { 446 | Record::UnknownType(rec_type).send(&mut &*sock)?; 447 | }, 448 | Record::GetValues(keys) => { 449 | Record::GetValuesResult(get_values(keys)) 450 | .send(&mut &*sock)? 451 | }, 452 | Record::BeginRequest { request_id, .. } => { 453 | Record::EndRequest { 454 | request_id: request_id, 455 | app_status: 0, 456 | protocol_status: ProtocolStatus::CantMpxConn, 457 | } 458 | .send(&mut &*sock)? 459 | } 460 | Record::AbortRequest { request_id } => { 461 | if id != request_id { 462 | continue; 463 | } 464 | aborted = true; 465 | break; 466 | } 467 | Record::Params { request_id, content } => { 468 | if id != request_id { 469 | continue; 470 | } 471 | if content.is_empty() { 472 | params.extend(read_pairs(&mut Cursor::new(&buf))?); 473 | break; 474 | } else { 475 | buf.extend(content); 476 | } 477 | }, 478 | _ => (), 479 | } 480 | } 481 | Ok(Request { 482 | sock: sock, 483 | id: id, 484 | role: role, 485 | params: params, 486 | aborted: aborted, 487 | status: 0, 488 | buf: Vec::new(), 489 | pos: 0, 490 | is_eof: false, 491 | filter_data: false, 492 | }) 493 | } 494 | 495 | pub fn role(&self) -> Role { 496 | self.role 497 | } 498 | 499 | /// Retrieves the value of the given parameter name. 500 | pub fn param(&self, key: &str) -> Option { 501 | self.params.get(key).map(|s| s.clone()) 502 | } 503 | 504 | /// Iterates over the FastCGI parameters. 505 | pub fn params(&self) -> Params { 506 | Box::new(self.params.iter().map(|(k, v)| (k.clone(), v.clone()))) 507 | } 508 | 509 | /// Standard input stream of the request. 510 | pub fn stdin(&mut self) -> Stdin { 511 | Stdin { req: self } 512 | } 513 | 514 | /// Standard output stream of the request. 515 | pub fn stdout(&mut self) -> Stdout { 516 | Stdout { req: self } 517 | } 518 | 519 | /// Standard error stream of the request. 520 | pub fn stderr(&mut self) -> Stderr { 521 | Stderr { req: self } 522 | } 523 | 524 | /// Checks if the client has closed the connection prematurely. 525 | /// 526 | /// The reliability of this method depends on whether the web server 527 | /// notifies such event (by sending the `FCGI_REQUEST_ABORTED` record) to 528 | /// the FastCGI application. This value is updated synchronously; the 529 | /// update may only be triggered by reading from stdin. 530 | pub fn is_aborted(&self) -> bool { 531 | self.aborted 532 | } 533 | 534 | /// Reports the specified exit code to the web server. 535 | /// 536 | /// This will consume the Request object. If you finish processing the 537 | /// Request object without calling `exit`, it is assumed that the exit code 538 | /// is 0. 539 | pub fn exit(mut self, code: i32) { 540 | self.status = code; 541 | } 542 | } 543 | 544 | impl Drop for Request { 545 | fn drop(&mut self) { 546 | Record::Stdout { 547 | request_id: self.id, 548 | content: Vec::new(), 549 | }.send(&mut &*self.sock).unwrap_or(()); 550 | Record::Stderr { 551 | request_id: self.id, 552 | content: Vec::new() 553 | }.send(&mut &*self.sock).unwrap_or(()); 554 | Record::EndRequest { 555 | request_id: self.id, 556 | app_status: self.status, 557 | protocol_status: ProtocolStatus::RequestComplete, 558 | }.send(&mut &*self.sock).unwrap_or(()); 559 | } 560 | } 561 | 562 | fn run_transport(handler: F, transport: &mut Transport) where 563 | F: Fn(Request) + Send + Sync + 'static { 564 | let addrs: Option> = match std::env::var("FCGI_WEB_SERVER_ADDRS") { 565 | Ok(value) => Some(value.split(',').map(|s| s.to_owned()).collect()), 566 | Err(std::env::VarError::NotPresent) => None, 567 | Err(e) => Err(e).unwrap(), 568 | }; 569 | let handler = Arc::new(handler); 570 | loop { 571 | let sock = match transport.accept() { 572 | Ok(sock) => sock, 573 | Err(e) => panic!(e.to_string()), 574 | }; 575 | let allow = match addrs { 576 | Some(ref addrs) => match sock.peer() { 577 | Ok(ref addr) => addrs.contains(addr), 578 | Err(_) => false, 579 | }, 580 | None => true, 581 | }; 582 | if allow { 583 | let handler = handler.clone(); 584 | thread::spawn(move || { 585 | let sock = Rc::new(sock); 586 | loop { 587 | let (request_id, role, keep_conn) = Request::begin(&sock).unwrap(); 588 | handler(Request::new(sock.clone(), request_id, role).unwrap()); 589 | if !keep_conn { break; } 590 | } 591 | }); 592 | } 593 | } 594 | } 595 | 596 | #[cfg(unix)] 597 | /// Runs as a FastCGI process with the given handler. 598 | /// 599 | /// Available under Unix only. If you are using Windows, use `run_tcp` instead. 600 | pub fn run(handler: F) where F: Fn(Request) + Send + Sync + 'static { 601 | run_transport(handler, &mut Transport::new()) 602 | } 603 | 604 | #[cfg(unix)] 605 | /// Accepts requests from a user-supplied raw file descriptor. IPv4, IPv6, and 606 | /// Unix domain sockets are supported. 607 | /// 608 | /// Available under Unix only. 609 | pub fn run_raw(handler: F, raw_fd: std::os::unix::io::RawFd) where 610 | F: Fn(Request) + Send + Sync + 'static { 611 | run_transport(handler, &mut Transport::from_raw_fd(raw_fd)) 612 | } 613 | 614 | #[cfg(unix)] 615 | /// Accepts requests from a user-supplied TCP listener. 616 | pub fn run_tcp(handler: F, listener: &TcpListener) where 617 | F: Fn(Request) + Send + Sync + 'static { 618 | use std::os::unix::io::AsRawFd; 619 | run_transport(handler, &mut Transport::from_raw_fd(listener.as_raw_fd())) 620 | } 621 | 622 | #[cfg(windows)] 623 | /// Accepts requests from a user-supplied TCP listener. 624 | pub fn run_tcp(handler: F, listener: &TcpListener) where 625 | F: Fn(Request) + Send + Sync + 'static { 626 | run_transport(handler, &mut Transport::from_tcp(&listener)) 627 | } 628 | 629 | #[cfg(unix)] 630 | /// Check if the program is running as FastCGI. This check is not necessary if 631 | /// you use `run_tcp`. 632 | pub fn is_fastcgi() -> bool { 633 | Transport::new().is_fastcgi() 634 | } 635 | -------------------------------------------------------------------------------- /src/unix.rs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // // 3 | // Copyright (c) 2015 Mohd Tarmizi Mohd Affandi // 4 | // // 5 | // Permission is hereby granted, free of charge, to any person obtaining a // 6 | // copy of this software and associated documentation files (the // 7 | // "Software"), to deal in the Software without restriction, including // 8 | // without limitation the rights to use, copy, modify, merge, publish, // 9 | // distribute, sublicense, and/or sell copies of the Software, and to // 10 | // permit persons to whom the Software is furnished to do so, subject to // 11 | // the following conditions: // 12 | // // 13 | // The above copyright notice and this permission notice shall be included // 14 | // in all copies or substantial portions of the Software. // 15 | // // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // 19 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // 20 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // 21 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // 22 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 23 | // // 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | use libc as c; 27 | use std::io::{self, Read, Write}; 28 | use std::mem; 29 | use std::net::{Ipv4Addr, Ipv6Addr}; 30 | use std::os::unix::io::RawFd; 31 | use std::ptr::{null_mut}; 32 | 33 | const LISTENSOCK_FILENO: c::c_int = 0; 34 | 35 | pub struct Transport { 36 | inner: c::c_int, 37 | } 38 | 39 | impl Transport { 40 | pub fn new() -> Self { 41 | Self::from_raw_fd(LISTENSOCK_FILENO) 42 | } 43 | 44 | pub fn from_raw_fd(raw_fd: RawFd) -> Self { 45 | Transport { inner: raw_fd } 46 | } 47 | 48 | pub fn is_fastcgi(&self) -> bool { 49 | let res = unsafe { c::getpeername(self.inner, null_mut(), null_mut()) }; 50 | res == -1 && io::Error::last_os_error().raw_os_error() == Some(c::ENOTCONN) 51 | } 52 | 53 | pub fn accept(&mut self) -> io::Result { 54 | let res = unsafe { 55 | c::accept(self.inner, 0 as *mut _, 0 as *mut _) 56 | }; 57 | if res == -1 { 58 | Err(io::Error::last_os_error()) 59 | } else { 60 | Ok(Socket { inner: res }) 61 | } 62 | } 63 | } 64 | 65 | pub struct Socket { 66 | inner: c::c_int, 67 | } 68 | 69 | impl Socket { 70 | pub fn peer(&self) -> io::Result { 71 | unsafe { 72 | let mut ss = mem::zeroed::(); 73 | let mut len = mem::size_of::() as c::socklen_t; 74 | let res = c::getpeername( 75 | self.inner, 76 | &mut ss as *mut _ as *mut c::sockaddr, 77 | &mut len 78 | ); 79 | if res == -1 { 80 | return Err(io::Error::last_os_error()); 81 | } 82 | match ss.ss_family as c::c_int { 83 | c::AF_INET => { 84 | let sin = *(&ss as *const _ as *const c::sockaddr_in); 85 | let ip = mem::transmute::(sin.sin_addr); 86 | Ok(Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]).to_string()) 87 | }, 88 | c::AF_INET6 => { 89 | let sin = *(&ss as *const _ as *const c::sockaddr_in6); 90 | let ip = mem::transmute::(sin.sin6_addr); 91 | Ok(Ipv6Addr::new( 92 | ip[0], ip[1], ip[2], ip[3], 93 | ip[4], ip[5], ip[6], ip[7] 94 | ).to_string() 95 | ) 96 | }, 97 | c::AF_UNIX => Ok(String::new()), 98 | _ => Err(io::Error::new( 99 | io::ErrorKind::InvalidInput, 100 | "Unsupported FastCGI socket" 101 | )), 102 | } 103 | } 104 | } 105 | } 106 | 107 | impl Drop for Socket { 108 | fn drop(&mut self) { 109 | unsafe { c::shutdown(self.inner, c::SHUT_WR); } 110 | let mut buf = Vec::new(); 111 | self.read_to_end(&mut buf).ok(); 112 | unsafe { c::close(self.inner); } 113 | } 114 | } 115 | 116 | impl<'a> Read for &'a Socket { 117 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 118 | let res = unsafe { 119 | c::read( 120 | self.inner, 121 | buf.as_mut_ptr() as *mut c::c_void, 122 | buf.len() as c::size_t 123 | ) 124 | }; 125 | if res == -1 { 126 | Err(io::Error::last_os_error()) 127 | } else { 128 | Ok(res as usize) 129 | } 130 | } 131 | } 132 | 133 | impl<'a> Write for &'a Socket { 134 | fn write(&mut self, buf: &[u8]) -> io::Result { 135 | let res = unsafe { 136 | c::write( 137 | self.inner, 138 | buf.as_ptr() as *const c::c_void, 139 | buf.len() as c::size_t 140 | ) 141 | }; 142 | if res == -1 { 143 | Err(io::Error::last_os_error()) 144 | } else { 145 | Ok(res as usize) 146 | } 147 | } 148 | 149 | fn flush(&mut self) -> io::Result<()> { Ok(()) } 150 | } 151 | 152 | impl Read for Socket { 153 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 154 | (&*self).read(buf) 155 | } 156 | } 157 | 158 | impl Write for Socket { 159 | fn write(&mut self, buf: &[u8]) -> io::Result { 160 | (&*self).write(buf) 161 | } 162 | 163 | fn flush(&mut self) -> io::Result<()> { 164 | (&*self).flush() 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/windows.rs: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // // 3 | // Copyright (c) 2015 Mohd Tarmizi Mohd Affandi // 4 | // // 5 | // Permission is hereby granted, free of charge, to any person obtaining a // 6 | // copy of this software and associated documentation files (the // 7 | // "Software"), to deal in the Software without restriction, including // 8 | // without limitation the rights to use, copy, modify, merge, publish, // 9 | // distribute, sublicense, and/or sell copies of the Software, and to // 10 | // permit persons to whom the Software is furnished to do so, subject to // 11 | // the following conditions: // 12 | // // 13 | // The above copyright notice and this permission notice shall be included // 14 | // in all copies or substantial portions of the Software. // 15 | // // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // 19 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // 20 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // 21 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // 22 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // 23 | // // 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | use std::io::{self, Read, Write}; 27 | use std::net::{TcpListener, TcpStream, SocketAddr}; 28 | 29 | pub struct Transport<'a> { 30 | inner: &'a TcpListener, 31 | } 32 | 33 | impl<'a> Transport<'a> { 34 | pub fn from_tcp(listener: &'a TcpListener) -> Self { 35 | Transport { inner: listener } 36 | } 37 | 38 | pub fn accept(&mut self) -> io::Result { 39 | let (stream, _) = self.inner.accept()?; 40 | Ok(Socket { inner: stream }) 41 | } 42 | } 43 | 44 | pub struct Socket { 45 | inner: TcpStream, 46 | } 47 | 48 | impl Socket { 49 | pub fn peer(&self) -> io::Result { 50 | match self.inner.peer_addr()? { 51 | SocketAddr::V4(addr) => Ok(addr.ip().to_string()), 52 | SocketAddr::V6(addr) => Ok(addr.ip().to_string()), 53 | } 54 | } 55 | } 56 | 57 | impl<'a> Read for &'a Socket { 58 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 59 | (&self.inner).read(buf) 60 | } 61 | } 62 | 63 | impl<'a> Write for &'a Socket { 64 | fn write(&mut self, buf: &[u8]) -> io::Result { 65 | (&self.inner).write(buf) 66 | } 67 | 68 | fn flush(&mut self) -> io::Result<()> { 69 | (&self.inner).flush() 70 | } 71 | } 72 | 73 | impl Read for Socket { 74 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 75 | (&*self).read(buf) 76 | } 77 | } 78 | 79 | impl Write for Socket { 80 | fn write(&mut self, buf: &[u8]) -> io::Result { 81 | (&*self).write(buf) 82 | } 83 | 84 | fn flush(&mut self) -> io::Result<()> { 85 | (&*self).flush() 86 | } 87 | } 88 | --------------------------------------------------------------------------------