From 52e6473446d3fa3ab03586ce66d45cb99493c0a1 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sat, 4 Jul 2020 18:00:45 +0200 Subject: Generate docs v0.1.0 --- src/fastcgi_conduit/lib.rs.html | 119 ++++++++ src/fastcgi_conduit/request.rs.html | 577 ++++++++++++++++++++++++++++++++++++ src/fastcgi_conduit/server.rs.html | 305 +++++++++++++++++++ 3 files changed, 1001 insertions(+) create mode 100644 src/fastcgi_conduit/lib.rs.html create mode 100644 src/fastcgi_conduit/request.rs.html create mode 100644 src/fastcgi_conduit/server.rs.html (limited to 'src') diff --git a/src/fastcgi_conduit/lib.rs.html b/src/fastcgi_conduit/lib.rs.html new file mode 100644 index 0000000..9da5f1c --- /dev/null +++ b/src/fastcgi_conduit/lib.rs.html @@ -0,0 +1,119 @@ +lib.rs.html -- source
 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
+
+#![warn(missing_docs)]
+
+// Copyright (c) 2020  Teddy Wing
+//
+// This file is part of FastCGI-Conduit.
+//
+// FastCGI-Conduit is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// FastCGI-Conduit is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FastCGI-Conduit. If not, see <https://www.gnu.org/licenses/>.
+
+//! # fastcgi-conduit
+//!
+//! FastCGI-Conduit provides a [Conduit] interface to FastCGI, enabling a
+//! high-level API for FastCGI applications.
+//!
+//!
+//! ## Example
+//!
+//! ``` rust
+//! use conduit::{header, Body, RequestExt, Response};
+//! use fastcgi_conduit::Server;
+//!
+//!
+//! fn main() {
+//!     Server::start(handler);
+//! }
+//!
+//! fn handler(_req: &mut dyn RequestExt) -> std::io::Result<Response<Body>> {
+//!     Ok(
+//!         Response::builder()
+//!             .header(header::CONTENT_TYPE, "text/html")
+//!             .body(Body::from_static(b"<h1>Hello</h1>"))
+//!             .unwrap()
+//!     )
+//! }
+//! ```
+//!
+//!
+//! [Conduit]: ../conduit/index.html
+
+extern crate conduit;
+extern crate fastcgi;
+extern crate http;
+extern crate log;
+
+mod request;
+mod server;
+
+pub use server::Server;
+
+
\ No newline at end of file diff --git a/src/fastcgi_conduit/request.rs.html b/src/fastcgi_conduit/request.rs.html new file mode 100644 index 0000000..6ca8ef6 --- /dev/null +++ b/src/fastcgi_conduit/request.rs.html @@ -0,0 +1,577 @@ +request.rs.html -- source
  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
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+
+// Copyright (c) 2020  Teddy Wing
+//
+// This file is part of FastCGI-Conduit.
+//
+// FastCGI-Conduit is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// FastCGI-Conduit is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FastCGI-Conduit. If not, see <https://www.gnu.org/licenses/>.
+
+use std::io;
+use std::io::Read;
+use std::net::SocketAddr;
+
+use inflector::cases::traincase::to_train_case;
+
+use snafu::{ResultExt, Snafu};
+
+
+/// Errors parsing a FastCGI request.
+#[derive(Debug, Snafu)]
+pub enum Error {
+    /// The HTTP method is invalid.
+    #[snafu(context(false))]
+    InvalidMethod { source: http::method::InvalidMethod },
+
+    /// An invalid HTTP header name.
+    #[snafu(context(false))]
+    InvalidHeaderName { source: conduit::header::InvalidHeaderName },
+
+    /// An invalid HTTP header value.
+    #[snafu(context(false))]
+    InvalidHeaderValue { source: conduit::header::InvalidHeaderValue },
+
+    /// An invalid remote address.
+    #[snafu(context(false))]
+    InvalidRemoteAddr { source: RemoteAddrError },
+}
+
+/// A convenience `Result` that contains a request `Error`.
+pub type RequestResult<T, E = Error> = std::result::Result<T, E>;
+
+/// Errors parsing an HTTP remote address.
+#[derive(Debug, Snafu)]
+pub enum RemoteAddrError {
+    /// Error parsing the address part.
+    #[snafu(display("Could not parse address {}: {}", address, source))]
+    AddrParseError {
+        address: String,
+        source: std::net::AddrParseError,
+    },
+
+    /// Error parsing the port part.
+    #[snafu(display("Could not parse port {}: {}", port, source))]
+    PortParseError {
+        port: String,
+        source: std::num::ParseIntError
+    },
+}
+
+
+/// Wraps a [`fastcgi::Request`][fastcgi::Request] to implement
+/// [`conduit::RequestExt`][conduit::RequestExt].
+///
+/// [fastcgi::Request]: ../../fastcgi/struct.Request.html
+/// [conduit::RequestExt]: ../../conduit/trait.RequestExt.html
+pub struct FastCgiRequest<'a> {
+    request: &'a mut fastcgi::Request,
+    http_version: conduit::Version,
+    host: String,
+    method: conduit::Method,
+    headers: conduit::HeaderMap,
+    path: String,
+    query: Option<String>,
+    remote_addr: SocketAddr,
+    content_length: Option<u64>,
+    extensions: conduit::Extensions,
+}
+
+impl<'a> FastCgiRequest<'a> {
+    /// Create a new `FastCgiRequest`.
+    pub fn new(request: &'a mut fastcgi::Request) -> RequestResult<Self> {
+        let version = Self::version(request);
+        let host = Self::host(request);
+        let method = Self::method(request)?;
+        let headers = Self::headers(request.params())?;
+        let path = Self::path(request);
+        let query = Self::query(request);
+        let remote_addr = Self::remote_addr(request)?;
+        let content_length = Self::content_length(request);
+
+        Ok(Self {
+            request: request,
+            http_version: version,
+            host: host,
+            method: method,
+            headers: headers,
+            path: path,
+            query: query,
+            remote_addr: remote_addr,
+            content_length: content_length,
+            extensions: conduit::TypeMap::new(),
+        })
+    }
+
+    /// Extract the HTTP version.
+    fn version(request: &fastcgi::Request) -> conduit::Version {
+        match request.param("SERVER_PROTOCOL").unwrap_or_default().as_str() {
+            "HTTP/0.9" => conduit::Version::HTTP_09,
+            "HTTP/1.0" => conduit::Version::HTTP_10,
+            "HTTP/1.1" => conduit::Version::HTTP_11,
+            "HTTP/2.0" => conduit::Version::HTTP_2,
+            "HTTP/3.0" => conduit::Version::HTTP_3,
+            _ => conduit::Version::default(),
+        }
+    }
+
+    /// Get the request scheme (HTTP or HTTPS).
+    fn scheme(&self) -> conduit::Scheme {
+        let scheme = self.request.param("REQUEST_SCHEME").unwrap_or_default();
+
+        if scheme == "https" {
+            conduit::Scheme::Https
+        } else {
+            conduit::Scheme::Http
+        }
+    }
+
+    /// Get the HTTP host.
+    ///
+    /// This looks like `localhost:8000`.
+    fn host(request: &fastcgi::Request) -> String {
+        request.param("HTTP_HOST").unwrap_or_default()
+    }
+
+    /// Get the HTTP method (GET, HEAD, POST, etc.).
+    fn method(
+        request: &fastcgi::Request
+    ) -> Result<conduit::Method, http::method::InvalidMethod> {
+        conduit::Method::from_bytes(
+            request.param("REQUEST_METHOD")
+                .unwrap_or_default()
+                .as_bytes()
+        )
+    }
+
+    /// Build a map of request headers.
+    fn headers(params: fastcgi::Params) -> RequestResult<conduit::HeaderMap> {
+        let mut map = conduit::HeaderMap::new();
+        let headers = Self::headers_from_params(params);
+
+        for (name, value) in headers
+            .iter()
+            .map(|(name, value)| (name.as_bytes(), value.as_bytes()))
+        {
+            map.append(
+                conduit::header::HeaderName::from_bytes(name)?,
+                conduit::header::HeaderValue::from_bytes(value)?,
+            );
+        }
+
+        Ok(map)
+    }
+
+    /// Extract headers from request params. Transform these into pairs of
+    /// canonical header names and values.
+    fn headers_from_params(params: fastcgi::Params) -> Vec<(String, String)> {
+        return params
+            .filter(|(key, _)| key.starts_with("HTTP_"))
+            .map(|(key, value)| {
+                let key = key.get(5..).unwrap_or_default();
+                let key = &key.replace("_", "-");
+                let key = &to_train_case(&key);
+
+                (key.to_owned(), value)
+            })
+            .collect()
+    }
+
+    /// Get the URI path.
+    ///
+    /// Returns `/path` when the URI is `http://localhost:8000/path?s=query`.
+    /// When the path is empty, returns `/`.
+    fn path(request: &fastcgi::Request) -> String {
+        match request.param("SCRIPT_NAME") {
+            Some(p) => p,
+            None => "/".to_owned(),
+        }
+    }
+
+    /// Get the URI query string.
+    ///
+    /// Returns `s=query&lang=en` when the URI is
+    /// `http://localhost:8000/path?s=query&lang=en`.
+    fn query(request: &fastcgi::Request) -> Option<String> {
+        request.param("QUERY_STRING")
+    }
+
+    /// Get the remote address of the request.
+    fn remote_addr(request: &fastcgi::Request) -> Result<SocketAddr, RemoteAddrError> {
+        let addr = request.param("REMOTE_ADDR").unwrap_or_default();
+        let port = request.param("REMOTE_PORT").unwrap_or_default();
+
+        Ok(
+            SocketAddr::new(
+                addr.parse().context(AddrParseError { address: addr })?,
+                port.parse().context(PortParseError { port })?,
+            )
+        )
+    }
+
+    /// Get the request's content length.
+    fn content_length(request: &fastcgi::Request) -> Option<u64> {
+        request.param("CONTENT_LENGTH").and_then(|l| l.parse().ok())
+    }
+}
+
+impl<'a> Read for FastCgiRequest<'a> {
+    /// Read from the underlying FastCGI request's [`Stdin`][Stdin]
+    ///
+    /// [Stdin]: ../../fastcgi/struct.Stdin.html
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        self.request.stdin().read(buf)
+    }
+}
+
+impl<'a> conduit::RequestExt for FastCgiRequest<'a> {
+   fn http_version(&self) -> conduit::Version {
+       self.http_version
+   }
+
+   fn method(&self) -> &conduit::Method {
+       &self.method
+   }
+
+   fn scheme(&self) -> conduit::Scheme {
+       self.scheme()
+   }
+
+   fn host(&self) -> conduit::Host<'_> {
+       conduit::Host::Name(&self.host)
+   }
+
+   fn virtual_root(&self) -> std::option::Option<&str> {
+       None
+   }
+
+   fn path(&self) -> &str {
+       &self.path
+   }
+
+   fn query_string(&self) -> std::option::Option<&str> {
+       self.query.as_ref()
+           .map(|p| p.as_str())
+   }
+
+   fn remote_addr(&self) -> std::net::SocketAddr {
+       self.remote_addr
+   }
+
+   fn content_length(&self) -> std::option::Option<u64> {
+       self.content_length
+   }
+
+   fn headers(&self) -> &conduit::HeaderMap {
+       &self.headers
+   }
+
+   fn body(&mut self) -> &mut (dyn std::io::Read) {
+       self
+   }
+
+   fn extensions(&self) -> &conduit::Extensions {
+       &self.extensions
+   }
+
+   fn mut_extensions(&mut self) -> &mut conduit::Extensions {
+       &mut self.extensions
+   }
+}
+
+
\ No newline at end of file diff --git a/src/fastcgi_conduit/server.rs.html b/src/fastcgi_conduit/server.rs.html new file mode 100644 index 0000000..1208e7d --- /dev/null +++ b/src/fastcgi_conduit/server.rs.html @@ -0,0 +1,305 @@ +server.rs.html -- source
  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
+143
+144
+145
+146
+147
+148
+149
+150
+151
+
+// Copyright (c) 2020  Teddy Wing
+//
+// This file is part of FastCGI-Conduit.
+//
+// FastCGI-Conduit is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// FastCGI-Conduit is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FastCGI-Conduit. If not, see <https://www.gnu.org/licenses/>.
+
+use std::io;
+use std::io::Write;
+
+use conduit::Handler;
+
+use log::error;
+
+use snafu::{ResultExt, Snafu};
+
+use crate::request;
+
+
+/// The HTTP version used by the server.
+const HTTP_VERSION: &'static str = "HTTP/1.1";
+
+
+/// Wraps server errors.
+#[derive(Debug, Snafu)]
+pub enum Error {
+    /// I/O write errors during response output.
+    #[snafu(context(false))]
+    Write { source: io::Error },
+
+    /// Error building the request into a [`FastCgiRequest`][FastCgiRequest].
+    ///
+    /// [FastCgiRequest]: ../request/struct.FastCgiRequest.html
+    #[snafu(display("Couldn't build request: {}", source))]
+    RequestBuilder { source: request::Error },
+
+    /// Error building a [`conduit::Response`][conduit::Response].
+    ///
+    /// [conduit::Response]: ../../conduit/struct.Response.html
+    #[snafu(display("Couldn't parse response: {}", source))]
+    ConduitResponse { source: conduit::BoxError },
+}
+
+
+/// The application server that interfaces with FastCGI.
+pub struct Server;
+
+impl Server {
+    /// Start the server.
+    ///
+    /// Start the main [`fastcgi::run`][fastcgi::run] process to listen for
+    /// requests and handle them using `handler`.
+    ///
+    /// [fastcgi::run]: ../../fastcgi/fn.run.html
+    pub fn start<H: Handler + 'static + Sync>(handler: H) -> Server {
+        fastcgi::run(move |mut raw_request| {
+            match handle_request(&mut raw_request, &handler) {
+                Ok(_) => (),
+                Err(e) => match e {
+                    // Ignore write errors as clients will have closed the
+                    // connection by this point.
+                    Error::Write { .. } => error!("Write error: {}", e),
+
+                    Error::RequestBuilder { .. } => {
+                        error!("Unable to build request: {}", e);
+
+                        internal_server_error(&mut raw_request.stdout())
+                    },
+                    Error::ConduitResponse { .. } => {
+                        error!("Error getting response: {}", e);
+
+                        internal_server_error(&mut raw_request.stdout())
+                    },
+                }
+            }
+        });
+
+        Server{}
+    }
+}
+
+/// Given a raw FastCGI request and a Conduit handler, get a response from the
+/// handler to write to a FastCGI response.
+fn handle_request<H>(
+    mut raw_request: &mut fastcgi::Request,
+    handler: &H,
+) -> Result<(), Error>
+where H: Handler + 'static + Sync
+{
+    let mut request = request::FastCgiRequest::new(&mut raw_request)
+        .context(RequestBuilder)?;
+    let response = handler.call(&mut request);
+
+    let mut stdout = raw_request.stdout();
+
+    let (head, body) = response
+        .context(ConduitResponse)?
+        .into_parts();
+
+    write!(
+        &mut stdout,
+        "{} {} {}\r\n",
+        HTTP_VERSION,
+        head.status.as_str(),
+        head.status.canonical_reason().unwrap_or("UNKNOWN"),
+    )?;
+
+    for (name, value) in head.headers.iter() {
+        write!(&mut stdout, "{}: ", name)?;
+        stdout.write(value.as_bytes())?;
+        stdout.write(b"\r\n")?;
+    }
+
+    stdout.write(b"\r\n")?;
+
+    match body {
+        conduit::Body::Static(slice) =>
+            stdout.write(slice).map(|_| ())?,
+        conduit::Body::Owned(vec) =>
+            stdout.write(&vec).map(|_| ())?,
+        conduit::Body::File(mut file) =>
+            io::copy(&mut file, &mut stdout).map(|_| ())?,
+    };
+
+    Ok(())
+}
+
+/// Write a 500 internal server error to `w`.
+fn internal_server_error<W: Write>(mut w: W) {
+    let code = conduit::StatusCode::INTERNAL_SERVER_ERROR;
+
+    write!(
+        w,
+        "{} {} {}\r\n{}\r\n\r\n",
+        HTTP_VERSION,
+        code,
+        code.canonical_reason().unwrap_or_default(),
+        "Content-Length: 0",
+    )
+        .unwrap_or_else(|e| error!("Write error: {}", e))
+}
+
+
\ No newline at end of file -- cgit v1.2.3