std1.0.0-beta-7 SafeC docs

Source http/headers.sc

View in the repository

// SafeC Standard Library — HttpHeaders implementation (see headers.h).
#pragma once
#include <std/http/headers.h>
#include <std/collections/string.h>
#include <std/collections/vec.h>

namespace std {

static char hh_lower_(char c) {
    if (c >= 'A' && c <= 'Z') { return (char)(c + 32); }
    return c;
}

int http_name_eq(const &char a, const &char b) {
    unsafe {
        unsigned long i = 0UL;
        while (a[i] != (char)0 && hh_lower_(a[i]) == hh_lower_(b[i])) { i = i + 1UL; }
        return a[i] == (char)0 && b[i] == (char)0;
    }
}

struct String http_header_param(const &char value, const &char name) {
    struct String out = String::new();
    unsafe {
        const char* s = value;
        unsigned long i = 0UL;
        // Skip the main value.
        while (s[i] != (char)0 && s[i] != ';') {
            if (s[i] == '"') { i = i + 1UL; while (s[i] != (char)0 && s[i] != '"') { i = i + 1UL; } }
            if (s[i] != (char)0) { i = i + 1UL; }
        }
        while (s[i] == ';') {
            i = i + 1UL;
            while (hh_is_blank_(s[i])) { i = i + 1UL; }
            unsigned long ka = i;
            while (s[i] != (char)0 && s[i] != '=' && s[i] != ';') { i = i + 1UL; }
            unsigned long kb = i;
            while (kb > ka && hh_is_blank_(s[kb - 1UL])) { kb = kb - 1UL; }
            int same = 1;
            unsigned long k = 0UL;
            while (ka + k < kb) {
                if (name[k] == (char)0 || hh_lower_(s[ka + k]) != hh_lower_(name[k])) { same = 0; break; }
                k = k + 1UL;
            }
            if (same && name[k] != (char)0) { same = 0; }
            if (s[i] != '=') { continue; }
            i = i + 1UL;
            while (hh_is_blank_(s[i])) { i = i + 1UL; }
            struct String v = String::new();
            if (s[i] == '"') {
                i = i + 1UL;
                while (s[i] != (char)0 && s[i] != '"') {
                    if (s[i] == '\\' && s[i + 1UL] != (char)0) { i = i + 1UL; }
                    v.push_char(s[i]);
                    i = i + 1UL;
                }
                if (s[i] == '"') { i = i + 1UL; }
                while (s[i] != (char)0 && s[i] != ';') { i = i + 1UL; }
            } else {
                unsigned long va = i;
                while (s[i] != (char)0 && s[i] != ';') { i = i + 1UL; }
                unsigned long vb = i;
                while (vb > va && hh_is_blank_(s[vb - 1UL])) { vb = vb - 1UL; }
                v.push_n_(&s[va], vb - va);
            }
            if (same) { out.free(); return v; }
            v.free();
        }
    }
    return out;
}

struct String http_header_main_value(const &char value) {
    struct String out = String::new();
    unsafe {
        unsigned long i = 0UL;
        while (hh_is_blank_(value[i])) { i = i + 1UL; }
        while (value[i] != (char)0 && value[i] != ';') { out.push_char(hh_lower_(value[i])); i = i + 1UL; }
        while (out.length() > 0UL && hh_is_blank_(out.as_ptr()[out.length() - 1UL])) { out.truncate(out.length() - 1UL); }
    }
    return out;
}

struct HttpHeaders HttpHeaders::new() {
    struct HttpHeaders h;
    h.fields = Vec::new();
    return h;
}

static int hh_is_blank_(char c) { return c == ' ' || c == '\t'; }

struct HttpHeaders HttpHeaders::parse(const &char raw) {
    struct HttpHeaders h = HttpHeaders::new();
    unsafe {
        const char* s = raw;
        unsigned long pos = 0UL;
        while (s[pos] != (char)0) {
            unsigned long end = pos;
            while (s[end] != (char)0 && s[end] != '\n') { end = end + 1UL; }
            unsigned long next = s[end] == '\n' ? end + 1UL : end;
            unsigned long e = end;
            if (e > pos && s[e - 1UL] == '\r') { e = e - 1UL; }
            if (e > pos && hh_is_blank_(s[pos]) && h.fields.length() > 0UL) {
                // obs-fold: continuation of the previous field's value.
                unsigned long a = pos;
                while (a < e && hh_is_blank_(s[a])) { a = a + 1UL; }
                unsigned long b = e;
                while (b > a && hh_is_blank_(s[b - 1UL])) { b = b - 1UL; }
                if (b > a) {
                    &HttpHeaderField last = h.fields.at(h.fields.length() - 1UL);
                    last.value.push_char(' ');
                    last.value.push_n_(&s[a], b - a);
                }
            } else {
                unsigned long colon = pos;
                while (colon < e && s[colon] != ':') { colon = colon + 1UL; }
                if (colon < e) {
                    unsigned long ka = pos;
                    unsigned long kb = colon;
                    while (ka < kb && hh_is_blank_(s[ka])) { ka = ka + 1UL; }
                    while (kb > ka && hh_is_blank_(s[kb - 1UL])) { kb = kb - 1UL; }
                    unsigned long va = colon + 1UL;
                    unsigned long vb = e;
                    while (va < vb && hh_is_blank_(s[va])) { va = va + 1UL; }
                    while (vb > va && hh_is_blank_(s[vb - 1UL])) { vb = vb - 1UL; }
                    if (kb > ka) {
                        struct HttpHeaderField f;
                        f.name = String::from_n_(&s[ka], kb - ka);
                        f.value = String::from_n_(&s[va], vb - va);
                        h.fields.push(f);
                    }
                }
            }
            pos = next;
        }
    }
    return h;
}

unsigned long HttpHeaders::length() const { return self.fields.length(); }

const ?&char HttpHeaders::get_nth(const &char name, unsigned long n) const {
    unsigned long seen = 0UL;
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        if (http_name_eq(f.name.as_ptr(), name)) {
            if (seen == n) { return f.value.as_ptr(); }
            seen = seen + 1UL;
        }
        i = i + 1UL;
    }
    return null;
}

const ?&char HttpHeaders::get(const &char name) const { return self.get_nth(name, 0UL); }

const &char HttpHeaders::get_or(const &char name, const &char fallback) const {
    match (self.get_nth(name, 0UL)) {
        case some(v): { return v; }
        case null: { return fallback; }
    }
    return fallback;
}

unsigned long HttpHeaders::count(const &char name) const {
    unsigned long n = 0UL;
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        if (http_name_eq(self.fields.at(i).name.as_ptr(), name)) { n = n + 1UL; }
        i = i + 1UL;
    }
    return n;
}

struct String HttpHeaders::get_joined(const &char name) const {
    struct String out = String::new();
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        if (http_name_eq(f.name.as_ptr(), name)) {
            if (!out.is_empty()) { out.push(", "); }
            out.push_str(&f.value);
        }
        i = i + 1UL;
    }
    return out;
}

int HttpHeaders::has(const &char name) const { return self.count(name) > 0UL; }

// Is 'token' one of the comma-separated items of value v?
static int hh_list_has_(const char* v, const char* token) {
    unsafe {
        unsigned long i = 0UL;
        unsigned long tl = 0UL;
        while (token[tl] != (char)0) { tl = tl + 1UL; }
        while (v[i] != (char)0) {
            while (v[i] == ' ' || v[i] == '\t' || v[i] == ',') { i = i + 1UL; }
            unsigned long a = i;
            while (v[i] != (char)0 && v[i] != ',') { i = i + 1UL; }
            unsigned long b = i;
            while (b > a && (v[b - 1UL] == ' ' || v[b - 1UL] == '\t')) { b = b - 1UL; }
            if (b - a == tl && tl > 0UL) {
                unsigned long k = 0UL;
                while (k < tl && hh_lower_(v[a + k]) == hh_lower_(token[k])) { k = k + 1UL; }
                if (k == tl) { return 1; }
            }
        }
    }
    return 0;
}

int HttpHeaders::contains_token(const &char name, const &char token) const {
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        if (http_name_eq(f.name.as_ptr(), name) && hh_list_has_(f.value.as_ptr(), token)) { return 1; }
        i = i + 1UL;
    }
    return 0;
}

const ?&char HttpHeaders::name_at(unsigned long i) const {
    if (i >= self.fields.length()) { return null; }
    return self.fields.at(i).name.as_ptr();
}

const ?&char HttpHeaders::value_at(unsigned long i) const {
    if (i >= self.fields.length()) { return null; }
    return self.fields.at(i).value.as_ptr();
}

void HttpHeaders::append(const &char name, const &char value) {
    struct HttpHeaderField f;
    f.name = String::from(name);
    f.value = String::from(value);
    self.fields.push(f);
}

void HttpHeaders::set(const &char name, const &char value) {
    int done = 0;
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        if (http_name_eq(f.name.as_ptr(), name)) {
            if (!done) {
                std::replace(&f.value, String::from(value)).drop();
                done = 1;
            } else {
                drop(self.fields.remove(i));
                continue;
            }
        }
        i = i + 1UL;
    }
    if (!done) { self.append(name, value); }
}

unsigned long HttpHeaders::remove(const &char name) {
    unsigned long n = 0UL;
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        if (http_name_eq(f.name.as_ptr(), name)) {
            drop(self.fields.remove(i));
            n = n + 1UL;
            continue;
        }
        i = i + 1UL;
    }
    return n;
}

void HttpHeaders::clear() {
    self.fields.clear();   // and each field's strings
}

void HttpHeaders::write_to(&String out) const {
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        out.push_str(&f.name);
        out.push(": ");
        out.push_str(&f.value);
        out.push("\r\n");
        i = i + 1UL;
    }
}

struct String HttpHeaders::to_raw() const {
    struct String out = String::new();
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        if (i > 0UL) { out.push("\r\n"); }
        out.push_str(&f.name);
        out.push(": ");
        out.push_str(&f.value);
        i = i + 1UL;
    }
    return out;
}

struct HttpHeaders HttpHeaders::clone() const {
    struct HttpHeaders h = HttpHeaders::new();
    unsigned long i = 0UL;
    while (i < self.fields.length()) {
        &HttpHeaderField f = self.fields.at(i);
        h.append(f.name.as_ptr(), f.value.as_ptr());
        i = i + 1UL;
    }
    return h;
}

void HttpHeaders::free() { self.drop(); }

void HttpHeaders::drop() {
    self.clear();
    self.fields.free();
}

} // namespace std