fastcgi_sdk/
lib.rs

1//! A safe Rust binding to the FastCGI SDK. For examples, see the shipped
2//! binaries.
3//!
4//! The current version uses the C library of the FastCGI SDK. The plan is to
5//! rewrite it in Rust completely, removing this dependency and improving the
6//! interface.
7//!
8//! The binding has near zero overhead compared to the C library. The
9//! differences are:
10//!
11//!  1. It does one extra llocation per request, for ABI portability reasons.
12//!  2. It does unnecessary `strlen` calls, because of unfortunate limitations
13//!     in the standard library `CStr` implementation.
14
15extern crate libc;
16
17pub mod ffi;
18
19use ffi::*;
20
21use std::ffi::CStr;
22use std::io;
23use std::marker::PhantomData;
24use std::os::unix::io::AsRawFd;
25use std::sync::{ONCE_INIT, Once};
26
27static FCGX_INIT: Once = ONCE_INIT;
28
29/// An HTTP message exchange. This is a pair of a request and a response.
30#[derive(Debug)]
31pub struct Exchange {
32    request: *mut FCGX_Request,
33}
34
35impl Exchange {
36    /// Create an exchange from a FastCGI request. The exchange will take
37    /// ownership of the request, and free it with `free`.
38    ///
39    /// You probably want to use `accept` instead.
40    pub unsafe fn new(request: *mut FCGX_Request) -> Self {
41        Exchange{request}
42    }
43
44    /// Accept a request from a listener. This can be a TCP listener or a Unix
45    /// domain socket listener. Do not pass an already-accepted socket; that
46    /// will not work.
47    ///
48    /// The reason this method exists, rather than requiring the caller to
49    /// accept the connection from the listener, is that the C library of the
50    /// FastCGI SDK does not expose a function that does not itself accept the
51    /// connection.
52    pub fn accept<L>(listener: &L) -> io::Result<Self>
53        where L: AsRawFd {
54        unsafe {
55            FCGX_INIT.call_once(|| {
56                let status = FCGX_Init();
57                assert!(status == 0);
58            });
59
60            let request = FCGX_ABICompat_MallocRequest();
61            if request.is_null() {
62                return Err(io::Error::last_os_error());
63            }
64
65            let file_descriptor = listener.as_raw_fd();
66            let status = FCGX_InitRequest(request, file_descriptor, 0);
67            if status != 0 {
68                libc::free(request);
69                return Err(io::Error::last_os_error());
70            }
71
72            let status = FCGX_Accept_r(request);
73            if status == -1 {
74                FCGX_Free(request, 0);
75                libc::free(request);
76                return Err(io::Error::last_os_error());
77            }
78
79            Ok(Self::new(request))
80        }
81    }
82
83    /// Return the FastCGI environment. These include the request method and the
84    /// request header, and various information about the server.
85    pub fn environment(&self) -> Environment {
86        Environment::new(self)
87    }
88
89    /// Return the request body for this exchange. You can call this method
90    /// multiple times, and all returned writers will write to the same buffer
91    /// and socket.
92    pub fn request_body(&self) -> RequestBody {
93        RequestBody::new(self)
94    }
95
96    /// Return the response for this exchange. You can call this method multiple
97    /// times, and all returned readers will read from the same buffer and
98    /// socket.
99    pub fn response(&self) -> Response {
100        Response::new(self)
101    }
102}
103
104impl Drop for Exchange {
105    fn drop(&mut self) {
106        unsafe {
107            FCGX_Finish_r(self.request);
108            libc::free(self.request);
109        }
110    }
111}
112
113unsafe impl Send for Exchange {
114}
115
116/// A FastCGI environment. This is an iterator that returns strings in the form
117/// `name=value`.
118#[derive(Clone, Debug)]
119pub struct Environment<'a> {
120    envp: FCGX_ParamArray,
121    phantom: PhantomData<&'a ()>,
122}
123
124impl<'a> Environment<'a> {
125    fn new(exchange: &Exchange) -> Self {
126        unsafe {
127            let envp = FCGX_ABICompat_RequestEnvp(exchange.request);
128            Environment{envp, phantom: PhantomData}
129        }
130    }
131}
132
133impl<'a> Iterator for Environment<'a> {
134    type Item = &'a CStr;
135
136    fn next(&mut self) -> Option<Self::Item> {
137        unsafe {
138            if (*self.envp).is_null() {
139                return None;
140            }
141
142            let entry = CStr::from_ptr(*self.envp);
143            self.envp = self.envp.offset(1);
144            Some(entry)
145        }
146    }
147}
148
149/// The body of the request of an exchange.
150#[derive(Debug)]
151pub struct RequestBody<'a> {
152    stream: *mut FCGX_Stream,
153    phantom: PhantomData<&'a ()>,
154}
155
156impl<'a> RequestBody<'a> {
157    fn new(exchange: &Exchange) -> Self {
158        unsafe {
159            let stream = FCGX_ABICompat_RequestIn(exchange.request);
160            RequestBody{stream, phantom: PhantomData}
161        }
162    }
163}
164
165impl<'a> io::Read for RequestBody<'a> {
166    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
167        unsafe {
168            let actual_read = FCGX_GetStr(buf.as_mut_ptr() as *mut i8,
169                                          buf.len() as i32, self.stream);
170            if FCGX_GetError(self.stream) != 0 {
171                return Err(io::Error::last_os_error());
172            }
173            Ok(actual_read as usize)
174        }
175    }
176}
177
178/// A response for an exchange. This includes both the response status line,
179/// headers, and body.
180#[derive(Debug)]
181pub struct Response<'a> {
182    stream: *mut FCGX_Stream,
183    phantom: PhantomData<&'a ()>,
184}
185
186impl<'a> Response<'a> {
187    fn new(exchange: &Exchange) -> Self {
188        unsafe {
189            let stream = FCGX_ABICompat_RequestOut(exchange.request);
190            Response{stream, phantom: PhantomData}
191        }
192    }
193}
194
195impl<'a> io::Write for Response<'a> {
196    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
197        unsafe {
198            let actual_written = FCGX_PutStr(buf.as_ptr() as *const i8,
199                                             buf.len() as i32, self.stream);
200            if actual_written == -1 {
201                return Err(io::Error::last_os_error());
202            }
203            Ok(actual_written as usize)
204        }
205    }
206
207    fn flush(&mut self) -> io::Result<()> {
208        unsafe {
209            let status = FCGX_FFlush(self.stream);
210            if status == -1 {
211                return Err(io::Error::last_os_error());
212            }
213            Ok(())
214        }
215    }
216}