1extern 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#[derive(Debug)]
31pub struct Exchange {
32 request: *mut FCGX_Request,
33}
34
35impl Exchange {
36 pub unsafe fn new(request: *mut FCGX_Request) -> Self {
41 Exchange{request}
42 }
43
44 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 pub fn environment(&self) -> Environment {
86 Environment::new(self)
87 }
88
89 pub fn request_body(&self) -> RequestBody {
93 RequestBody::new(self)
94 }
95
96 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#[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#[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#[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}