Skip to content

Commit

Permalink
Merge branch 'actix'
Browse files Browse the repository at this point in the history
  • Loading branch information
w4 committed Mar 14, 2022
2 parents 2bbab69 + 776bec1 commit e6642a7
Show file tree
Hide file tree
Showing 13 changed files with 1,788 additions and 1,179 deletions.
2,415 changes: 1,444 additions & 971 deletions Cargo.lock

Large diffs are not rendered by default.

29 changes: 18 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
[package]
name = "bin"
version = "1.0.5"
version = "2.0.0"
description = "a paste bin."
repository = "https://github.com/w4/bin"
license = "WTFPL OR 0BSD"
authors = ["Jordan Doyle <jordan@doyle.la>"]
edition = "2018"
edition = "2021"

[dependencies]
owning_ref = "0.4"
argh = "0.1"
log = "0.4"
pretty_env_logger = "0.4"
linked-hash-map = "0.5"
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "master" }
askama = "0.9"
lazy_static = "1.4"
rand = { version = "0.7", features = ["nightly"] }
once_cell = "1.10"
parking_lot = "0.12"
bytes = { version = "1.1", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
rand = { version = "0.8" }
gpw = "0.1"
syntect = "4.1"
serde_derive = "1.0"
tokio = { version = "0.2", features = ["sync", "macros"] }
async-trait = "0.1"
actix = "0.13"
actix-web = "4.0"
htmlescape = "0.3"
askama = "0.11"
bat = "0.20"
syntect = "4.6"
tokio = { version = "1.17", features = ["sync"] }
futures = "0.3"

[profile.release]
lto = true
Expand Down
9 changes: 4 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
FROM rust:1.34.2-slim-stretch AS builder
RUN rustup install nightly-x86_64-unknown-linux-gnu
FROM rust:1-slim AS builder

RUN apt update && apt install -y libclang-dev

COPY . /sources
WORKDIR /sources
RUN cargo +nightly build --release
RUN cargo build --release
RUN chown nobody:nogroup /sources/target/release/bin


FROM debian:stretch-slim
FROM debian:bullseye-slim
COPY --from=builder /sources/target/release/bin /pastebin

USER nobody
EXPOSE 8000
ENTRYPOINT ["/pastebin"]
ENTRYPOINT ["/pastebin", "0.0.0.0:8000"]
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ a paste bin.

A paste bin that's actually minimalist. No database requirement, no commenting functionality, no self-destructing or time bomb messages and no social media integration—just an application to quickly send snippets of text to people.

[bin](https://bin.gy/) is written in Rust in around 200 lines of code. It's fast, it's simple, there's code highlighting and you can ⌘+A without going to the 'plain' page. It's revolutionary in the paste bin industry, disrupting markets and pushing boundaries never seen before.
[bin](https://bin.gy/) is written in Rust in around 300 lines of code. It's fast, it's simple, there's code highlighting and you can ⌘+A without going to the 'plain' page. It's revolutionary in the paste bin industry, disrupting markets and pushing boundaries never seen before.

##### so how do you get bin?

Expand All @@ -29,9 +29,22 @@ $ ./bin

##### funny, what settings are there?

bin uses [rocket](https://rocket.rs) so you can add a [rocket config file](https://api.rocket.rs/v0.3/rocket/config/) if you like. You can set `ROCKET_PORT` in your environment if you want to change the default port (8820).
```
$ ./bin
Usage: bin [<bind_addr>] [--buffer-size <buffer-size>] [--max-paste-size <max-paste-size>]
bin's only configuration value is `BIN_BUFFER_SIZE` which defaults to 2000. Change this value if you want your bin to hold more pastes.
a pastebin.
Positional Arguments:
bind_addr socket address to bind to (default: 127.0.0.1:8820)
Options:
--buffer-size maximum amount of pastes to store before rotating (default:
1000)
--max-paste-size maximum paste size in bytes (default. 32kB)
--help display usage information
```

##### is there curl support?

Expand Down
56 changes: 56 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use actix_web::{body::BoxBody, http::header, http::StatusCode, web, HttpResponse, ResponseError};

use std::fmt::{Formatter, Write};

macro_rules! impl_response_error_for_http_resp {
($ty:ty, $path:expr, $status:expr) => {
impl ResponseError for $ty {
fn error_response(&self) -> HttpResponse {
HtmlResponseError::error_response(self)
}
}

impl std::fmt::Display for $ty {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", include_str!($path))
}
}

impl HtmlResponseError for $ty {
fn status_code(&self) -> StatusCode {
$status
}
}
};
}

#[derive(Debug)]
pub struct NotFound;

impl_response_error_for_http_resp!(NotFound, "../templates/404.html", StatusCode::NOT_FOUND);

#[derive(Debug)]
pub struct InternalServerError(pub Box<dyn std::error::Error>);

impl_response_error_for_http_resp!(
InternalServerError,
"../templates/500.html",
StatusCode::INTERNAL_SERVER_ERROR
);

pub trait HtmlResponseError: ResponseError {
fn status_code(&self) -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
}

fn error_response(&self) -> HttpResponse {
let mut resp = HttpResponse::new(HtmlResponseError::status_code(self));
let mut buf = web::BytesMut::new();
let _ = write!(&mut buf, "{}", self);
resp.headers_mut().insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("text/html; charset=utf-8"),
);
resp.set_body(BoxBody::new(buf))
}
}
51 changes: 35 additions & 16 deletions src/highlight.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
extern crate syntect;
use bat::assets::HighlightingAssets;
use once_cell::sync::Lazy;
use syntect::{
html::{ClassStyle, ClassedHTMLGenerator},
parsing::SyntaxSet,
};

use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
use syntect::html::{styled_line_to_highlighted_html, IncludeBackground};
use syntect::parsing::SyntaxSet;
thread_local!(pub static BAT_ASSETS: HighlightingAssets = HighlightingAssets::from_binary());

/// Takes the content of a paste and the extension passed in by the viewer and will return the content
/// highlighted in the appropriate format in HTML.
///
/// Returns `None` if the extension isn't supported.
pub fn highlight(content: &str, ext: &str) -> Option<String> {
lazy_static! {
static ref SS: SyntaxSet = SyntaxSet::load_defaults_newlines();
static ref TS: ThemeSet = ThemeSet::load_defaults();
}
static SS: Lazy<SyntaxSet> = Lazy::new(SyntaxSet::load_defaults_newlines);

BAT_ASSETS.with(|f| {
let ss = f.get_syntax_set().ok().unwrap_or(&SS);
let syntax = ss.find_syntax_by_extension(ext)?;
let mut html_generator =
ClassedHTMLGenerator::new_with_class_style(syntax, ss, ClassStyle::Spaced);
for line in LinesWithEndings(content.trim()) {
html_generator.parse_html_for_line_which_includes_newline(line);
}
Some(html_generator.finalize())
})
}

let syntax = SS.find_syntax_by_extension(ext)?;
let mut h = HighlightLines::new(syntax, &TS.themes["base16-ocean.dark"]);
let regions = h.highlight(content, &SS);
pub struct LinesWithEndings<'a>(&'a str);

Some(styled_line_to_highlighted_html(
&regions[..],
IncludeBackground::No,
))
impl<'a> Iterator for LinesWithEndings<'a> {
type Item = &'a str;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.0.is_empty() {
None
} else {
let split = self.0.find('\n').map_or(self.0.len(), |i| i + 1);
let (line, rest) = self.0.split_at(split);
self.0 = rest;
Some(line)
}
}
}
62 changes: 18 additions & 44 deletions src/io.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
extern crate gpw;
extern crate linked_hash_map;
extern crate owning_ref;
extern crate rand;

use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};

use actix_web::web::Bytes;
use linked_hash_map::LinkedHashMap;

use owning_ref::OwningRef;

use once_cell::sync::Lazy;
use parking_lot::RwLock;
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::cell::RefCell;
use std::env;

use tokio::sync::{RwLock, RwLockReadGuard};

type RwLockReadGuardRef<'a, T, U = T> = OwningRef<Box<RwLockReadGuard<'a, T>>, U>;
pub type PasteStore = RwLock<LinkedHashMap<String, Bytes>>;

lazy_static! {
static ref ENTRIES: RwLock<LinkedHashMap<String, String>> = RwLock::new(LinkedHashMap::new());
static ref BUFFER_SIZE: usize = env::var("BIN_BUFFER_SIZE")
.map(|f| f
.parse::<usize>()
.expect("Failed to parse value of BIN_BUFFER_SIZE"))
.unwrap_or(1000usize);
}
static BUFFER_SIZE: Lazy<usize> = Lazy::new(|| argh::from_env::<crate::BinArgs>().buffer_size);

/// Ensures `ENTRIES` is less than the size of `BIN_BUFFER_SIZE`. If it isn't then
/// `ENTRIES.len() - BIN_BUFFER_SIZE` elements will be popped off the front of the map.
///
/// During the purge, `ENTRIES` is locked and the current thread will block.
async fn purge_old() {
let entries_len = ENTRIES.read().await.len();

if entries_len > *BUFFER_SIZE {
let to_remove = entries_len - *BUFFER_SIZE;

let mut entries = ENTRIES.write().await;
fn purge_old(entries: &mut LinkedHashMap<String, Bytes>) {
if entries.len() > *BUFFER_SIZE {
let to_remove = entries.len() - *BUFFER_SIZE;

for _ in 0..to_remove {
entries.pop_front();
Expand All @@ -52,29 +31,24 @@ pub fn generate_id() -> String {
thread_rng()
.sample_iter(&Alphanumeric)
.take(6)
.collect::<String>()
.map(char::from)
.collect()
})
}

/// Stores a paste under the given id
pub async fn store_paste(id: String, content: String) {
purge_old().await;
pub fn store_paste(entries: &PasteStore, id: String, content: Bytes) {
let mut entries = entries.write();

purge_old(&mut entries);

ENTRIES.write().await.insert(id, content);
entries.insert(id, content);
}

/// Get a paste by id.
///
/// Returns `None` if the paste doesn't exist.
pub async fn get_paste(
id: &str,
) -> Option<RwLockReadGuardRef<'_, LinkedHashMap<String, String>, String>> {
pub fn get_paste(entries: &PasteStore, id: &str) -> Option<Bytes> {
// need to box the guard until owning_ref understands Pin is a stable address
let or = RwLockReadGuardRef::new(Box::new(ENTRIES.read().await));

if or.contains_key(id) {
Some(or.map(|x| x.get(id).unwrap()))
} else {
None
}
entries.read().get(id).map(Bytes::clone)
}
Loading

0 comments on commit e6642a7

Please sign in to comment.