Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TLS/HTTPS Support #68

Open
wants to merge 9 commits into
base: nightly
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Nightly workflow

on:
push:
branches:
- nightly

permissions:
contents: write

jobs:
test:
uses: ./.github/workflows/test.yml

package:
uses: ./.github/workflows/package.yml

publish:
uses: ./.github/workflows/publish.yml
secrets:
PREFIX_API_KEY: ${{ secrets.PREFIX_API_KEY }}
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release tag pipeline
name: Release workflow

on:
push:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ install_id
output

# misc
.vscode
.vscode
lightbug_http/rustls**
1 change: 1 addition & 0 deletions lightbug_http/libc.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from utils import StaticTuple
from memory import UnsafePointer
from sys.ffi import external_call
from sys.info import sizeof
from memory import memcpy
Expand Down
2 changes: 2 additions & 0 deletions lightbug_http/net.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from sys.info import sizeof
from utils import StringRef
from memory import UnsafePointer
from lightbug_http.strings import NetworkType
from lightbug_http.io.bytes import Bytes
from lightbug_http.io.sync import Duration
Expand Down
1 change: 1 addition & 0 deletions lightbug_http/sys/net.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from utils import StaticTuple
from memory import UnsafePointer
from sys.info import sizeof
from sys.ffi import external_call
from lightbug_http.net import (
Expand Down
4,958 changes: 4,575 additions & 383 deletions magic.lock

Large diffs are not rendered by default.

27 changes: 22 additions & 5 deletions mojoproject.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
[project]
authors = ["saviorand"]
channels = ["conda-forge", "https://conda.modular.com/max", "https://repo.prefix.dev/mojo-community"]
channels = ["conda-forge", "https://conda.modular.com/max-nightly", "https://repo.prefix.dev/mojo-community-nightly", "https://repo.prefix.dev/mojo-community"]
description = "Simple and fast HTTP framework for Mojo!"
name = "lightbug_http"
platforms = ["osx-arm64", "linux-64"]
version = "0.1.4"
license = "MIT"
license-file = "LICENSE"
homepage = "https://github.com/saviorand/lightbug_http"
repository = "https://github.com/saviorand/lightbug_http"

[tasks]
build = { cmd = "rattler-build build --recipe recipes -c https://conda.modular.com/max -c conda-forge --skip-existing=all", env = {MODULAR_MOJO_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo"} }
publish = { cmd = "bash scripts/publish.sh", env = { PREFIX_API_KEY = "$PREFIX_API_KEY" } }
build = { cmd = "bash scripts/build.sh nightly", env = {MODULAR_MOJO_IMPORT_PATH = "$CONDA_PREFIX/lib/mojo"} }
publish = { cmd = "bash scripts/publish.sh mojo-community-nightly", env = { PREFIX_API_KEY = "$PREFIX_API_KEY" } }
bp = { depends_on=["build", "publish"] }
template = { cmd = "magic run python scripts/templater.py" }
test = { cmd = "magic run mojo test -I . tests" }
bench = { cmd = "magic run mojo bench.mojo" }
bench_server = { cmd = "magic run mojo build bench_server.mojo && ./bench_server ; rm bench_server" }
format = { cmd = "magic run mojo format -l 120 lightbug_http" }

[dependencies]
max = ">=24.5.0,<25"
gojo = "0.1.9"
small_time = "0.1.3"
gojo = ">=0.1.12"
small_time = ">=1.24.6.0.dev2024092705"

[feature.nightly]
channels = ["conda-forge", "https://conda.modular.com/max-nightly", "https://repo.prefix.dev/mojo-community-nightly", "https://repo.prefix.dev/mojo-community"]

[feature.nightly.dependencies]
max = ">=24.5.0,<25"
gojo = ">=0.1.12"
small_time = ">=1.24.6.0.dev2024092705"

[environments]
nightly = ["nightly"]
28 changes: 28 additions & 0 deletions recipes/recipe.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json

context:
version: "13.4.2"

package:
name: {{NAME}}
version: {{VERSION}}

source:
- path: .
- path: ../{{LICENSE_FILE}}

build:
script:
- mkdir -p ${PREFIX}/lib/mojo
- magic run mojo package {{NAME}} -o ${PREFIX}/lib/mojo/{{NAME}}.mojopkg

requirements:
run:
{{DEPENDENCIES}}

about:
homepage: {{HOMEPAGE}}
license: {{LICENSE}}
license_file: {{LICENSE_FILE}}
summary: {{DESCRIPTION}}
repository: {{REPOSITORY}}
30 changes: 0 additions & 30 deletions recipes/recipe.yaml

This file was deleted.

11 changes: 11 additions & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -e
# The environment to build the package for. Usually "default", but might be "nightly" or others.
ENVIRONMENT="${1-default}"
if [[ "${ENVIRONMENT}" == "--help" ]]; then
echo "Usage: ENVIRONMENT - Argument 1 corresponds with the environment you wish to build the package for."
exit 0
fi
magic run template -m "${ENVIRONMENT}"
rattler-build build -r src -c https://conda.modular.com/max -c conda-forge --skip-existing=all
rm recipes/recipe.yaml
4 changes: 3 additions & 1 deletion scripts/publish.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#!/bin/bash

CHANNEL=${1-'mojo-community'}
echo "Publishing packages to: $CHANNEL"
# ignore errors because we want to ignore duplicate packages
for file in $CONDA_BLD_PATH/**/*.conda; do
magic run rattler-build upload prefix -c "mojo-community" "$file" || true
magic run rattler-build upload prefix -c "$CHANNEL" "$file" || true
done

rm $CONDA_BLD_PATH/**/*.conda
70 changes: 70 additions & 0 deletions scripts/templater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import tomllib
import argparse
from typing import Any


def build_dependency_list(dependencies: dict[str, str]) -> list[str]:
deps: list[str] = []
for name, version in dependencies.items():
start = 0
operator = "=="
if version[0] in {'<', '>'}:
if version[1] != "=":
operator = version[0]
start = 1
else:
operator = version[:2]
start = 2

deps.append(f"- {name} {operator} {version[start:]}")

return deps


def main():
# Configure the parser to receive the mode argument.
parser = argparse.ArgumentParser(description='Generate a recipe for the project.')
parser.add_argument('-m',
'--mode',
default="default",
help="The environment to generate the recipe for. Defaults to 'default' for the standard vers"
)
args = parser.parse_args()

# Load the project configuration and recipe template.
config: dict[str, Any]
with open('mojoproject.toml', 'rb') as f:
config = tomllib.load(f)

recipe: str
with open('recipes/template.yaml', 'r') as f:
recipe = f.read()

# Replace the placeholders in the recipe with the project configuration.
recipe = recipe \
.replace("{{NAME}}", config["project"]["name"]) \
.replace("{{DESCRIPTION}}", config["project"]["description"]) \
.replace("{{LICENSE}}", config["project"]["license"]) \
.replace("{{LICENSE_FILE}}", config["project"]["license-file"]) \
.replace("{{HOMEPAGE}}", config["project"]["homepage"]) \
.replace("{{REPOSITORY}}", config["project"]["repository"]) \
.replace("{{VERSION}}", config["project"]["version"])

# Dependencies are the only notable field that changes between environments.
dependencies: dict[str, str]
match args.mode:
case "default":
dependencies = config["dependencies"]
case _:
dependencies = config["feature"][args.mode]["dependencies"]

deps = build_dependency_list(dependencies)
recipe = recipe.replace("{{DEPENDENCIES}}", "\n".join(deps))

# Write the final recipe.
with open('recipes/recipe.yaml', 'w+') as f:
recipe = f.write(recipe)


if __name__ == '__main__':
main()
73 changes: 73 additions & 0 deletions test.mojo
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import lightbug_http.rustls as rls
import os
from utils import StringSlice, Span
from collections import Optional, InlineArray
from memory import Arc

alias DEMO_OK = 0
alias DEMO_AGAIN = 1
alias DEMO_EOF = 2
alias DEMO_ERROR = 3


@value
struct ConnData:
var fd: Int
var verify_arg: String
var data: List[UInt8]


fn log_cb(level: Int, message: StringSlice):
print("Log level:", level, "Message:", message)



fn default_provider_with_custom_ciphersuite(
custom_ciphersuite_name: StringSlice,
) raises -> rls.CryptoProvider:
custom_ciphersuite = Optional[rls.SupportedCiphersuite]()
for suite in rls.default_crypto_provider_ciphersuites():
if not suite:
raise Error("failed to get ciphersuite")
if suite.get_name() == custom_ciphersuite_name:
custom_ciphersuite = suite

if not custom_ciphersuite:
raise Error(
"failed to select custom ciphersuite: "
+ str(custom_ciphersuite_name)
)

provider_builder = rls.CryptoProviderBuilder()
providers = List(custom_ciphersuite.value())
provider_builder.set_cipher_suites(providers)

return provider_builder^.build()


fn main() raises:
var cert_path = "/etc/ssl/cert.pem"
if not os.setenv("CA_FILE", cert_path):
raise Error("Failed to set CA_FILE environment variable")

custom_provider = default_provider_with_custom_ciphersuite(
"TLS13_CHACHA20_POLY1305_SHA256"
)
tls_versions = List[UInt16](0x0303, 0x0304)
config_builder = rls.ClientConfigBuilder(custom_provider, tls_versions)
server_cert_root_store_builder = rls.RootCertStoreBuilder()
server_cert_root_store_builder.load_roots_from_file(cert_path)
server_root_cert_store = server_cert_root_store_builder^.build()
server_cert_verifier_builder = rls.WebPkiServerCertVerifierBuilder(
server_root_cert_store
)
server_cert_verifier = server_cert_verifier_builder^.build()
config_builder.set_server_verifier(server_cert_verifier)
var alpn = List[Span[UInt8, StaticConstantLifetime]]("http/1.1".as_bytes_span())
config_builder.set_alpn_protocols(alpn)
client_config = config_builder^.build()
host = "www.google.com"
port = "443"
path = "/"
conn = rls.ClientConnection(client_config, host)
# result = do_request(client_config, host, port, path)
Loading