Giter Site home page Giter Site logo

gemini's Introduction

Gemini

Building blocks for creating Gemini servers and clients.

Unlike geminim, this library does not handle serving files, cgi or configuration.

Example

This library supports both Socket and AsyncSocket servers and clients. The async variants are prefixed with Async. Since Gemini requires TLS, you have to provide a certificate and private key associated with your domain name.

Example server:

import gemini

proc handle(req: Request) =
  await req.respond(Success, "text/gemini", "# Hello world")

var server = newGeminiServer(certFile = "fullchain.pem", keyFile = "privkey.pem")
server.serve(Port(1965), handle)

Example async server:

import asyncdispatch
import gemini

proc handle(req: AsyncRequest) {.async.} =
  await req.respond(Success, "text/gemini", "# Hello world")

var server = newAsyncGeminiServer(certFile = "fullchain.pem", keyFile = "privkey.pem")
waitFor server.serve(Port(1965), handle)

Example client:

import gemini

try:
  let client = newGeminiClient()
  let response = await client.request("gemini://gemini.circumlunar.space")

  echo "status: " & $response.status
  echo "meta: " & response.meta
  echo "body: " & response.body
except GeminiError:
  echo getCurrentExceptionMsg()

Example async client:

import asyncdispatch
import gemini

proc main() {.async.} =
  try:
    let client = newAsyncGeminiClient()
    let response = await client.request("gemini://gemini.circumlunar.space")

    echo "status: " & $response.status
    echo "meta: " & response.meta
    echo "body: " & await response.body
  except GeminiError:
    echo getCurrentExceptionMsg()

waitFor main()

Documentation

See https://gemini.circumlunar.space/docs/specification.html for the protocol specification.

Create a new server:

proc newGeminiServer*(reuseAddr = true; reusePort = false, certFile = "", keyFile = ""): GeminiServer
proc newAsyncGeminiServer*(reuseAddr = true; reusePort = false, certFile = "", keyFile = ""): AsyncGeminiServer

Listen on a given port:

proc serve*(server: GeminiServer, port = Port(1965), callback: proc (request: Request) {.closure.}, address = "")
proc serve*(server: AsyncGeminiServer, port = Port(1965), callback: proc (request: AsyncRequest): Future[void] {.closure.}, address = "") {.async.}

The callback is given a request which contains the url requested and a certificate if the client provided one.

Note that when address contains a column, ":" the code assumes that you are specifying an IPv6 address (such as :: or ::1 which correspond to 0.0.0.0 and 127.0.0.1). On Linux hosts, IPv4 requests are automatically mapped to IPv6 when listening to "::". If the request url cannot be parsed, the server replies with status 50 INTERNAL ERROR.

Use respond() to send back a response:

proc respond*(req: Request, status: Status, meta: string, body: string = "")
proc respond*(req: AsyncRequest, status: Status, meta: string, body: string = "") {.async.}

Supported status codes:

type Status* = enum
  Input = 10
  SensitiveInput = 11
  Success = 20
  TempRedirect = 30
  Redirect = 31
  TempError = 40
  ServerUnavailable = 41
  CGIError = 42
  ProxyError = 43
  Slowdown = 44
  Error = 50
  NotFound = 51
  Gone = 52
  ProxyRefused = 53
  MalformedRequest = 59
  CertificateRequired = 60
  CertificateUnauthorized = 61
  CertificateNotValid = 62

To create a new client:

proc newGeminiClient*(maxRedirects = 5, certFile = "", keyFile = ""): GeminiClient
proc newAsyncGeminiClient*(maxRedirects = 5, certFile = "", keyFile = ""): AsyncGeminiClient

You can provide a certFile and keyFile to be sent along the query.

Then you can submit a request to a "gemini://" url

proc request*(client: GeminiClient, url: string): Response
proc request*(client: AsyncGeminiClient, url: string): Future[AsyncResponse] {.async.}

The response contains three fields: the returned status code and the associated meta, and the server certificate. Redirects are handled.

You cat get the response body with:

proc body*(response: Response): string 
proc body*(response: Response): Future[string] {.async.}

If an exception occurs such as a protocol error, you will get a GeminiError exception.

Working with certificates

Both requests and responses have a few functions for handling certificates:

  • hasCertificate() is true when there was a certificate
  • isVerified() is true when the certificate chain was verfied up to a known root (as with a https client)
  • isSelfSigned() is true when the certificate is self signed

You are supposed to handle self-signed certificates in a "Trust On First Use" fashion by building a whitelist like openssh.

You can inspect a certificate with $req.certificate, get the associated common name with req.certificate.commonName(), and get a sha256 fingerprint with req.certificate.fingerprint().

Warning

The TLS implementation in Nim is not well tested, it may contain vulnerabilities. Note that the current code is too permissive and accepts SSL2/SSL3 handshakes.

Also, since net/asyncnet do not expose the underlying SSL sockets, we use a terrible hack which complexifies the code quite a bit. See nim-lang/Nim#17021 for details.

Todo

  • Handle client certificates
  • Trust self-signed certificates
  • Non-async interface
  • Parse text/gemini

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.