Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/bung87/scorper
scorper is a micro and elegant web framework written in Nim
https://github.com/bung87/scorper
Last synced: 26 days ago
JSON representation
scorper is a micro and elegant web framework written in Nim
- Host: GitHub
- URL: https://github.com/bung87/scorper
- Owner: bung87
- License: apache-2.0
- Created: 2020-11-22T13:03:55.000Z (almost 4 years ago)
- Default Branch: devel
- Last Pushed: 2024-08-22T10:57:47.000Z (3 months ago)
- Last Synced: 2024-08-22T12:24:17.448Z (3 months ago)
- Language: Nim
- Homepage:
- Size: 700 KB
- Stars: 79
- Watchers: 11
- Forks: 6
- Open Issues: 17
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-nim - scorper - A micro and elegant web framework written in Nim. (Web / Frameworks)
README
# SCORPER ![Build Status](https://github.com/bung87/scorper/workflows/Test/badge.svg)
![scorper](artwork/scorper.png)
[travis]: https://travis-ci.org/bung87/scorper.svg?branch=master
**Elegant, performant, asynchronous micro web framework written in Nim**
Built using [chronos](https://github.com/status-im/nim-chronos.git) and other powerful libraries.
**Note**: `scorper` is built using `chronos` which implements an `async`/`await` paradigm that is, at its core, different and therefore incompatible with the nim std library `asyncdispatch`'s `async`; this conflict is irresolvable and you will have to ensure your project dependencies support chronos async/await (note: many libraries do offer this, often using a compiler switch).
> `scorper` will self contain manageable dependency source code for accelerated development.
## Compiler flags
### -ssl
Compile with: `-d:ssl`
Then, using the proc `newScorper`, you will need to pass `isSecurity = true`,`privateKey`, `certificate`
### Other
``` nim
const GzipEnable {.booldefine.} = true
const HttpRequestBufferSize* {.intdefine.} = 2.Kb
const HttpServer {.strdefine.} = "scorper"
const TimeOut {.intdefine.} = 300 # in seconds
const HttpHeadersLength* {.intdefine.} = int(HttpRequestBufferSize / 32)
# 32 is sizeof MofuHeader
const gzipMinLength* {.intdefine.} = 20
```## Usage
details see here [usage](./USAGE.md)
### hello world
### `serve` with callback
``` nim
import scorperconst port{.intdefine.} = 8888
proc cb(req: Request) {.async.} =
## See Request type to see fields accessible by callbackslet headers = {"Content-type": "text/plain"}
await req.resp("Hello, World!", headers.newHttpHeaders())when isMainModule:
let address = "127.0.0.1:" & $portwaitFor serve(address, cb)
## waitFor is a chronos implementation
## See chronos for options around executing async procs
```### `newScorper` with router or callback
``` nim
when isMainModule:
let r = newRouter[ScorperCallback]()
# Relys on `StaticDir` environment variable
r.addRoute(serveStatic, "get", "/static/*$")
let address = "127.0.0.1:8888"
let flags = {ReuseAddr}
var server = newScorper(address, r, flags)
server.start()
waitFor server.join()
```### use `route` pragma
``` nim
proc handler(req: Request) {.route("get","/one"),async.} = discard
proc handler2(req: Request) {.route(["get","post"],"/multi"),async.} = discard
let r = newRouter[ScorperCallback]()
r.addRoute(handler)
r.addRoute(handler2)
```### responds depends on request mime
``` nim
import scorperproc cb(req: Request) {.async.} =
var headers = newHttpHeaders()
acceptMime(req, ext, headers):
## The template will automatically define the second parametercase ext
of "html": await req.resp("Hello World", headers)
of "txt": await req.resp("Hello World", headers)
else:
headers["Content-Type"] = "text/html"
await req.resp("Hello World", headers)when isMainModule:
let address = "127.0.0.1:8888"
waitFor serve(address, cb)
```### Middleware
Middleware can be implemented by importing `scorper/scorpermacros` (the import and definition of your middleware procedures **MUST** occur before importing scorper; see the note labeled *IMPORTANT* below)
>#### preMiddleware
>
>Procedure that is injected after the Request object fields are filled but BEFORE your callback.
>
>#### postMiddleware
>
>Procedure that is injected AFTER your callback (and therefore after your *response*).#### Declaring middleware:
```nim
import scorper/scorpermacrosproc middleWare(req: Request): Future[bool] {.async, postMiddleware.} =
## Do things here
return false
## return false to prevent calling the next middleware
```Middleware procedures return `bool` to indicate whether to continue the call-chain to the next middleware.
see [tmiddleware.nim](tests/tmiddleware.nim) for more
## Types
``` nim
type
Request* = ref object
meth*: HttpMethod
headers*: HttpHeaders
protocol*: tuple[major, minor: int]
url*: Url
hostname*: string
ip*: string
params*: Table[string, string]ScorperCallback* = proc (req: Request): Future[void] {.closure, gcsafe.}
Scorper* = ref object of StreamServer
# inherited (partial)
sock*: AsyncFD # Socket
local*: TransportAddress # Address
status*: ServerStatus # Current server status
flags*: set[ServerFlags] # Flags
errorCode*: OSErrorCode
MiddlewareProc* = proc (req: Request): Future[bool]
```## Error handling & Exception effects
As [`scorper`](https://github.com/bung87/scorper) make use of [`chronos`](https://github.com/status-im/nim-chronos) for asynchronous procedures, the handling of errors and exceptions within this paradigm are of relevance.
> `chronos` currently offers minimal support for exception effects and `raises`
annotations. In general, during the `async` transformation, a generic
`except CatchableError` handler is added around the entire function being
transformed, in order to catch any exceptions and transfer them to the `Future`.
>
> Effectively, this means that while code can be compiled with
`{.push raises: [Defect]}`, the intended effect propagation and checking is
**disabled** for `async` functions.## Todos
- [ ] Parse JSON streamingly.
- [ ] CLI tool generate object oriented controller and routes.
- [x] Parse http request streamingly.
- [x] Parse form streamingly and lazyly.
- [x] Send file and attachement streamingly.
- [x] Http Basic auth, Bearer auth , Digest auth(planed).
- [x] Serve static files (env:StaticDir)
- [x] Parse JSON lazyly.
- [x] cookies module.
- [x] Better error control flow.
- [x] Auto render response respect client accepted content (acceptMime macro)type.
- [x] Chuncked file upload handle for large file.
- [x] https support
- [x] handle Resumable upload## Contribution
check Todo list upon and [issues](https://github.com/bung87/scorper/issues)clone this repository, run `nimble install stage` , if you already have `stage` installed, run `stage init`
`stage` is used for integrating with git commit hook, doing checking and code formatting.## Benchmark
[Web Frameworks Benchmark](https://web-frameworks-benchmark.netlify.app/result?l=nim)
[TechEmpower benchmarks](https://www.techempower.com/benchmarks/)
requires `wrk`
`nimble benchmark`
`nimble benchmarkserver`### Report
runs on my MBP Dual-Core Intel Core i5 2.7 GHz ,8 GB memory.
scorper: `1.0.2`
chronos: `3.0.1`
nim version: `1.5.1`, `1.4.4`
```
Running 30s test @ http://127.0.0.1:8888/
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.51ms 1.00ms 66.66ms 92.16%
Req/Sec 7.15k 518.66 10.94k 85.67%
854619 requests in 30.05s, 93.73MB read
Requests/sec: 28441.14
Transfer/sec: 3.12MB
```
### Conclusion*Scorper* is almost ten thousands faster than `jester` with stdlib. It is even a thousand times faster than `asynchttpserver`
## Misc features/limitations
The `mofuparser` use `SIMD` which relys on cpu support `SSE` or `AVX` instructions
## Related projects
[amysql](https://github.com/bung87/amysql) Async MySQL Connector write in pure Nim. (support chronos with compile flag `-d:ChronosAsync`)
## License
Apache License 2.0
The Scorper logo was created and released to the Scorper project under the CC BY-SA 4.0 by [https://ITwrx.org](https://ITwrx.org) and [https://LuciusRafi.com](https://LuciusRafi.com)