summary refs log tree commit diff stats
path: root/share/icons/hexchat-b.svg
diff options
context:
space:
mode:
authorBerke Viktor <bviktor@hexchat.org>2012-10-30 02:28:06 +0100
committerBerke Viktor <bviktor@hexchat.org>2012-10-30 02:28:06 +0100
commit9bf00ac214eb98e75bc7b40489a3319b81a9b052 (patch)
tree9e26401713fcc8688edd9a9a4e445a5049b6fe2b /share/icons/hexchat-b.svg
parent8fc9691704cf5ef6b110c301ff04640e56171955 (diff)
Don't provide untranslatable strings for translation
Diffstat (limited to 'share/icons/hexchat-b.svg')
0 files changed, 0 insertions, 0 deletions
='#n128'>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
// Testserver - static HTTP server for automated tests
// Copyright (C) 2021  Soni L.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program 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 Affero General Public License for more details.
// 
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Webserver helper for writing tests.

use std::convert::TryInto;
use std::io::Cursor;
use std::num::NonZeroU16;
use std::str::from_utf8_unchecked;
use std::sync::Arc;

use ar::Archive;

use tiny_http::Method;
use tiny_http::Response;
use tiny_http::Server as Httpd;

/// Helper to stop the server.
struct StopServer {
    server: Arc<Httpd>,
}

/// Stops the server.
impl Drop for StopServer {
    fn drop(&mut self) {
        self.server.unblock();
    }
}

/// A handle to a server. Also stops the server when dropped.
pub struct Server {
    inner: Arc<StopServer>,
}

impl Server {
    /// Returns the server's port number.
    pub fn port(&self) -> NonZeroU16 {
        let sv = &self.inner.server;
        sv.server_addr().port().try_into().unwrap()
    }
}

/// Spawns a webserver to serve the given archive.
///
/// The archive is an .a (ar) file, in a format that can be parsed by the ar
/// crate. Files can be base64, UTF-8, or ar: a `.b`, `.t`, or `.a` suffix to
/// the filename identifies the type. Directories are represented as nested ar
/// files.
///
/// # Panics
///
/// Panics if the server cannot be started.
///
/// # Returns
///
/// Returns a handle to be able to shut down the server, and the port number.
pub fn serve(archive: &'static str) -> Server {
    // TODO cache!
    // "Soni" base64-decodes to 0x4a 0x89 0xe2
    let server = Arc::new(Httpd::http("127.74.137.226:0").unwrap());
    let sv = server.clone();
    std::thread::spawn(move || {
        let archive = Cursor::new(archive);
        loop {
            let rq = match sv.recv() {
                Ok(rq) => rq,
                Err(_) => break,
            };
            
            match rq.method() {
                Method::Get | Method::Head => {
                    let path = rq.url();
                    // clean up the url
                    let path = path.split('#').next().unwrap();
                    let path = path.split('?').next().unwrap();
                    // normalize .
                    let path = path.split("%2E").collect::<Vec<_>>().join(".");
                    let path_parts = path.split('/');
                    let mut current = Some(Archive::new(archive.clone()));
                    let mut response = None;
                    for part in path_parts {
                        if response.is_some() {
                            // didn't run out of path_parts but somehow we
                            // found a valid response. invalidate it.
                            response = None;
                            break
                        }
                        let part = percent_encoding::percent_decode_str(part);
                        let part = part.decode_utf8();
                        if part.is_err() {
                            break
                        }
                        let part = part.unwrap();
                        let part: &str = &part;
                        let mut found = false;
                        let mut kind = "";
                        let mut size = 0usize;
                        while let Some(Ok(entry)) =
                            current.as_mut().unwrap().next_entry()
                        {
                            let name = entry.header().identifier();
                            // SAFETY: the input "file" is an &str already.
                            let name = unsafe { from_utf8_unchecked(name) };
                            size = entry.header().size() as usize;
                            if let Some(suffix) = name.strip_prefix(&part) {
                                // the suffix here isn't 'static, but we need
                                // one that is.
                                if suffix == ".a" {
                                    // dir
                                    found = true;
                                    kind = ".a";
                                    break
                                } else if suffix == ".b" {
                                    // base64
                                    found = true;
                                    kind = ".b";
                                    break
                                } else if suffix == ".t" {
                                    // text
                                    found = true;
                                    kind = ".t";
                                    break
                                } else {
                                    // carry on, we haven't found the file yet
                                }
                            }
                        }
                        if found {
                            let inner = current.take().unwrap();
                            let inner = inner.into_inner().unwrap();
                            let pos = inner.position() as usize;
                            let inner = inner.into_inner();
                            let inner = Cursor::new(&inner[pos-size..pos]);
                            if kind == ".a" {
                                // dir
                                current = Some(Archive::new(inner));
                            } else if kind == ".b" {
                                // base64
                                let inner = base64::decode(inner.into_inner());
                                let inner = Cursor::new(inner.unwrap());
                                response = Response::new(
                                    200.into(),
                                    vec![],
                                    Box::new(inner) as Box<dyn std::io::Read>,
                                    Some(size as usize),
                                    None,
                                ).into();
                            } else if kind == ".t" {
                                // text
                                response = Response::new(
                                    200.into(),
                                    vec![],
                                    Box::new(inner) as Box<dyn std::io::Read>,
                                    Some(size as usize),
                                    None,
                                ).into();
                            } else {
                                unreachable!();
                            }
                        }
                    }
                    if let Some(response) = response {
                        rq.respond(response).unwrap();
                    } else {
                        rq.respond(Response::empty(404)).unwrap();
                    }
                },
                e => {
                    eprintln!("Unexpected method: {}", e);
                }
            }
        }
    });
    Server {
        inner: Arc::new(StopServer { server }),
    }
}