Giter VIP home page Giter VIP logo

httpbeast's Introduction

httpbeast

A highly performant, multi-threaded HTTP 1.1 server written in Nim.

The main goal of this project is performance, when it was started the goal was to get the fastest possible HTTP server written in pure Nim, it has held the title of the fastest Nim HTTP server since its initial release. In 2018 HttpBeast reached the top 10 in the TechEmpower benchmarks beating many established HTTP servers implemented in other programming languages. Httpbeast has been used successfully in many projects, for example the Nim Forum.

ℹ️ Unless you know what you're doing (for example writing something resource constrained or your own web framework), you are better off using Jester (which is built on Httpbeast) or another web framework.

ℹ️ This HTTP server has been designed to utilise epoll-like OS APIs and as such does not support Windows by-design.

⚠️ This library is not yet hardened against common HTTP security exploits. If you're using it in production you should do so behind a reverse proxy like nginx.

Features

Current features include:

  • Built on the Nim selectors module which makes efficient use of epoll on Linux and kqueue on macOS.
  • Automatic parallelization, just make sure to compile with --threads:on.
  • Support for HTTP pipelining.
  • On-demand parser so that only the requested data is parsed.
  • Integration with Nim's asyncdispatch allowing async/await to be used in the request callback whenever necessary.

Getting started

Create a helloHttp.nimble file:

# Package

version       = "0.1.0"
author        = "Your Name"
description   = "Your Description"
license       = "MIT"
srcDir        = "src"
bin           = @["helloHttp"]


# Dependencies

requires "nim >= 1.0.0"
requires "httpbeast >= 0.4.0"

Create a src/helloHttp.nim file:

import options, asyncdispatch

import httpbeast

proc onRequest(req: Request): Future[void] =
  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      req.send("Hello World")
    else:
      req.send(Http404)

run(onRequest)

Run via: nimble c -r helloHttp.nim

httpbeast's People

Contributors

ajusa avatar bahm avatar bung87 avatar dom96 avatar hokamoto avatar iffy avatar inv2004 avatar ire4ever1190 avatar jasonprogrammer avatar kubo39 avatar metagn avatar pigmej avatar sthenic avatar thomastjdev avatar timotheecour avatar xbello avatar zedeus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

httpbeast's Issues

Not possible to append parameters to urls

When adding a paramater to the url HttpBeast parse the ? and returns a 404. When -d:dev is defined HttpBeast crash, while in -d:release it just returns 404.

Reference: Jester issue 146

Use case
When appending ?username=nimmer to the first route, the route will never be reached due to HttpBeast parses the ?.

import jester, asyncdispatch

routes:
  get "/projects/all":
    if @"username" == "nimmer":
       resp("OK")

  get "/projects/@projectid":
    if @"projectid" == "1": 
      resp("You have access")
    else:
      resp("No access")

Error

INFO Jester is making jokes at http://0.0.0.0:5000
Starting 1 threads
DEBUG GET /page2
DEBUG   200 OK Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style.css
DEBUG   304 Not Modified None[RawHeaders]
DEBUG   TCActionNothing
DEBUG GET /style2.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style3.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style4.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style5.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style6.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style7.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style8.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style9.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style10.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
DEBUG GET /style11.css?
DEBUG   404 Not Found Some(@[(key: "Content-Type", val: "text/html;charset=utf-8")])
paramurl.nim(3)          paramurl
jester.nim(462)          serve
httpbeast.nim(412)       run
httpbeast.nim(292)       eventLoop
asyncdispatch.nim(1511)  poll
asyncdispatch.nim(1277)  runOnce
asyncdispatch.nim(183)   processPendingCallbacks
asyncfutures.nim(216)    :anonymous
httpbeast.nim(212)       :anonymous
system.nim(3753)         failedAssertImpl
system.nim(3746)         raiseAssert
system.nim(2807)         sysFatal
Error: unhandled exception:
0 < len(data.sendQueue) - sendQueueBefore Request needs a response. [AssertionError]

Test code

import jester, asyncdispatch

routes:
  get "/page2":
    var html = "<h1>H1</h1><br>"
    html.add("<h2>H2</h2>")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style2.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style3.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style4.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style5.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style6.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style7.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style8.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style9.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style10.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style11.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style12.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style13.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style14.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style15.css?v=123\">")
    html.add("<link rel=\"stylesheet\" type=\"text/css\" href=\"style16.css?v=123\">")
    resp(html)

runForever()

Json content-type

tests/benchmark.nim:

/json route

content-type header should be set

Inconsistent behavior on run()

Hi @dom96,

When specifying settings (port, bind address, ...), the domain is set to AF_UNSPEC. However, without settings, the domain is set to AF_INET

This make

proc run*(onRequest: OnRequest, settings: Settings) =

and
proc run*(onRequest: OnRequest) {.inline.} =

have different behavior (except if we specify domain for the first one)

Regards,


Related to the-benchmarker/web-frameworks#4554

Using await in handlers appears to have a substantial performance penalty

Hello, I noticed some unexpected performance behavior when benchmarking HttpBeast and wanted to open an issue here in the hope of finding the cause or seeing if perhaps I'm misunderstanding something.

Here is the very simple HTTP server running on an Ubuntu 22.04 Linux server.

import httpbeast, std/asyncdispatch, std/options

proc onRequest(req: Request): Future[void] {.async.} =
  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      await sleepAsync(10)
      req.send(Http200, "abcdefghijklmnopqrstuvwxyz")
    else:
      req.send(Http404)

run(onRequest)

And the benchmarking command is wrk -t10 -c100 -d10s http://localhost:8080

No matter how I build this server, I always end up with a result of around Requests/sec: 99.82

This includes, -d:release,danger --threads:on,off

If I comment out the await sleepAsync(10) which is there to simulate the request taking a little time before responding (like talking to Redis or Posgres or RPC or whatever) the performance goes way way up, Requests/sec: 375,486.11

This is a pretty sizeable performance drop from using await, which is a normal thing to do in an async handler.

This issue appears to affect HttpBest, Jester, and Prologue that I have tested.

It appears what I have noticed may be related to #20 but I've not dug into that very deeply.

One more note:

The following simple AsyncHttpServer:

import std/asyncdispatch, std/asynchttpserver, std/strutils

proc main {.async.} =
  let server = newAsyncHttpServer()

  proc cb(request: Request) {.async.} =
    if request.url.path == "/":
      if request.reqMethod == HttpGet:
        {.gcsafe.}:
          await sleepAsync(10)
          await request.respond(Http200, "abcdefghijklmnopqrstuvwxyz")

  server.listen(Port(8080))

  while true:
    if server.shouldAcceptRequest():
      await server.acceptRequest(cb)
    else:
      await sleepAsync(500)

waitFor main()

Does not have this unexpected performance penalty, getting Requests/sec: 8,040.98

This result has been reproduced on two different Linux servers by two different people.

Compiler:

Nim Compiler Version 1.6.10 [Linux: amd64]
Compiled at 2022-11-21
Copyright (c) 2006-2021 by Andreas Rumpf

git hash: f1519259f85cbdf2d5ff617c6a5534fcd2ff6942
active boot switches: -d:release

problems pipelining requests with async routes

I can get screaming performance by pipelining simple requests through httpbeast however when my route has to await before it can send then any subsequent requests in the pipeline are blocked.

Is this because async requests are somhow routed through asynchttpserver rather than httpbeast and therefore do not support pipelining?

I have am usng a server based on httpbeast with my own router and a a fork of asyncpg which address some compatibility issues with the nim devel branch.

I have benchmarked my TechEmpower implementation (Whipower) against the FastHttp Go Implementation (FastPower) and so far, with non pipelined requests my server is just a little slower on json and text requests, almost 2x as fast on the database requests but about 5x slower on the fortune html template. (not sure how best to optimize this yet).

In Pipelined mode whip+beast is up to 25% for basic 'json' and 'plaintext' requests but all the other the db related tests are dead in the water.

All my tests so far are without threads as when I try enabling threads the performance always seems slower, and there are many more issues with async routes.

Threads aside, it would be great if you could provide some pointers to better support request pipelining in these cases. Any other tips on how to optimise this service would be also very usefull as it would be awesome to see Nim in the top rankings of all the techempower tests.

These are the latest results I have..

Non Pipelined Results:


Benchmarking fastpower

Running 10s test @ http://localhost:8080/json
Requests/sec:  75883.00

Running 10s test @ http://localhost:8080/plaintext
Requests/sec:  87931.02

Running 10s test @ http://localhost:8080/fortune
Requests/sec:  31852.30

Running 10s test @ http://localhost:8080/queries?queries=10
Requests/sec:   5357.72

Running 10s test @ http://localhost:8080/update?queries=10
Requests/sec:   2274.38

------------------------------------------

Benchmarking whipower

Running 10s test @ http://localhost:8080/json
Requests/sec:  70098.06

Running 10s test @ http://localhost:8080/plaintext
Requests/sec:  70161.92

Running 10s test @ http://localhost:8080/fortune
Requests/sec:  12953.60

Running 10s test @ http://localhost:8080/queries?queries=10
Requests/sec:  11350.36

Running 10s test @ http://localhost:8080/update?queries=10
Requests/sec:    5283.15

------------------------------------------

Pipelined Results:

Benchmarking fastpower

Running 10s test @ http://localhost:8080/json
Requests/sec: 190507.34

Running 10s test @ http://localhost:8080/plaintext
Requests/sec: 199306.43

Running 10s test @ http://localhost:8080/fortune
Requests/sec:  38954.41

Running 10s test @ http://localhost:8080/queries?queries=10
Requests/sec:   5670.90

Running 10s test @ http://localhost:8080/update?queries=10
Requests/sec:   2395.86

------------------------------------------

Benchmarking whipower

Running 10s test @ http://localhost:8080/json
Requests/sec: 254655.27

Running 10s test @ http://localhost:8080/plaintext
Requests/sec: 251232.13

Running 10s test @ http://localhost:8080/fortune
Requests/sec:    196.87

Running 10s test @ http://localhost:8080/queries?queries=10
Requests/sec:    104.55

Running 10s test @ http://localhost:8080/update?queries=10
Requests/sec:     33.87

------------------------------------------

Links:

There was an error when I used `httpbeast` and `asyncmysql` to get MYSQL data.

l use httpbeast and asyncmysql asynchronous connection database to get data. There are no errors in compiling, no errors in running, and the data can be obtained correctly when I access it with a browser. But when I use wrk -c10-d20s -T30s --latency http://192.168.1.2:8080/ for stress tests, the run goes wrong. Error :Error:unhandled exception:/home/lhf/. nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(321,10)getData.headersFinishedSelector not ready to send.[AssertionError]. I don't know if there's a problem with my code or the framework. please help to find the reason. Thank you.
My source code is as follows:

import net,asyncdispatch,asyncmysql
import httpbeast,options

const 
  MysqlHost = "localhost"
  MysqlPort= Port(3306)
  MysqlUser = "root"
  MysqlPassword = "123456"

type
  Replies = seq[tuple[packet: ResultPacket, rows: seq[string]]]

proc newPool*(connectLimit = 10):Future[AsyncMysqlPool] {.async.} =
  result = await openMysqlPool(AF_INET, MysqlPort, MysqlHost, MysqlUser,
    MysqlPassword, "db", connectionLimit = connectLimit)

proc closePool*(pool: AsyncMysqlPool) =
  close(pool)
  
proc dbQuery*(pool: AsyncMysqlPool, q: SqlQuery): Future[Replies] =
  var retFuture = newFuture[Replies]("dbQuery")
  result = retFuture

  proc finishCb(err: ref Exception, replies: Replies) {.async.} =
    if err == nil:
      complete(retFuture, replies)
    else:
      fail(retFuture, err)

  execQuery(pool, q, finishCb)

proc cx():Future[seq[string]] {.async.} =
  var pool = await newPool(2)
  let query = sql("select * from sys_user;")
  let replies = await pool.dbQuery(query)
  closePool(pool)
  result = replies[0].rows

proc onRequest(req: Request): Future[void] {.async.} =
  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      let data = await cx()
      const headers = "Content-Type: text/plain; charset=UTF-8"
      req.send(Http200, $data, headers)
    else:
      req.send(Http404)

run(onRequest)

Crash when "data.sendQueue.len == 0" or "data.sendQueue.len - sendQueueBefore > 0"

assert == false stops my program when running in -d:dev. This happens almost randomly - sometimes on the first request, other times on 10th request when I refresh rapidly.

I am loading quite a few local files:

  • 6 x CSS
  • 8 x JS
  • 5 x API calls which are initiated by 1 of the JS files with jQuery ajax as async

I have tried to disable the API calls, but no difference. My next step will be to strip out the files 1-by-1 and after that minimizing the size of the HTML page. Unless you can point me in another direction ;) ?

Errors

I am getting 2 different errors:

sendQueryBefore

jester.nim(462)          serve
httpbeast.nim(412)       run
httpbeast.nim(292)       eventLoop
asyncdispatch.nim(1511)  poll
asyncdispatch.nim(1277)  runOnce
asyncdispatch.nim(183)   processPendingCallbacks
asyncfutures.nim(216)    :anonymous
httpbeast.nim(212)       :anonymous
system.nim(3753)         failedAssertImpl
system.nim(3746)         raiseAssert
system.nim(2807)         sysFatal
Error: unhandled exception: 
0 < len(data.sendQueue) - sendQueueBefore Request needs a response. [AssertionError]

sendQueue

jester.nim(462)          serve
httpbeast.nim(412)       run
httpbeast.nim(285)       eventLoop
httpbeast.nim(162)       processEvents
system.nim(3753)         failedAssertImpl
system.nim(3746)         raiseAssert
system.nim(2807)         sysFatal
Error: unhandled exception: len(data.sendQueue) == 0  [AssertionError]

Solution

Compile options

compiling with --assertions:off or -d:release

Code

I can fix it by commenting out:

assert data.sendQueue.len == 0

and
assert data.sendQueue.len - sendQueueBefore > 0,

http 1.1 request smuggling

Vulnerability Details:

  1. Products: HttpBeast, Jester
  2. Tested Version: latest versions
  3. Vulnerability: Http 1.1 request smuggling
  4. PoC:

image

import jester

routes:
  get "/":
    resp "Hello world"


runForever()
  1. Impact:
    It is feasible to surreptitiously introduce a malicious request, leading to a disruption in the user experience. This vulnerability may result in various impacts such as Session Hijacking, Privilege Escalation, cache poisoning, and Denial of Service (DoS). Additionally, Self-XSS has the potential to escalate into a more severe Cross-Site Scripting (XSS) attack. In a broader context, this vulnerability can be interconnected with other vulnerabilities, amplifying their overall severity.

License?

Hi @dom96 , which license this library chooses? There is no license in this repo. I want to add windows support for httpbeast. I have finished a fragile prototype ioselectors enhancements for windows. It seems not too hard to add windows support(Though I don't know its performance, considering that wepoll uses IOCP, maybe it is not too bad.).

I have tested httpbeast in windows, helloworld seems to work(use wepoll).
image

Simple benchmark in windows using jmeter:

asynchttpserver (400 threads + 1s) and the throughput is 13388/s

httpbeast (600 threads + 1s) and the throughput is 22486/s

image

slower with threads on

Running the server with threads appears to introduce significant performance penalties..

With --threads = on (8 threads) :

Running 10s test @ http://localhost:8000/text
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   282.46us   71.12us   1.45ms   79.08%
    Req/Sec    17.51k   764.96    18.69k    80.69%
  352091 requests in 10.10s, 46.34MB read
Requests/sec:  34859.58
Transfer/sec:      4.59MB

Without threads (1 thread):

Running 10s test @ http://localhost:8000/text
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   200.23us   40.78us   0.97ms   85.91%
    Req/Sec    24.58k     0.92k   25.83k    82.67%
  494158 requests in 10.10s, 65.03MB read
Requests/sec:  48926.05
Transfer/sec:      6.44MB

Not as Fast as Java/Vertx - Configurable buffer sizss?

Recent testing shows with larger request bodies (500 bytes+), Java/Vertx is faster and has more consistent response timings. I think this is in part because with vertx I'm able to tune the TCP send and receive buffer sizes for the application - where can I find those buffers in the httpbeast source code to experiment? Looks like current buffer size is 256 bytes?

On phone, will post numbers later.

Random segmentation fault when using orc and threads more than 1

This minimal code should reproduce the issue.

# hello.nim
import options, asyncdispatch, logging

import httpbeast

proc onRequest(req: Request): Future[void] =
  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      req.send("Hello World")
    else:
      req.send(Http404)

if logging.getHandlers().len == 0:
  addHandler(logging.newConsoleLogger())
  setLogFilter(when defined(release): lvlInfo else: lvlDebug)

run(onRequest, httpbeast.initSettings(numThreads=2))
nim c -r --gc:orc -d:releae --threads:on hello.nim
  • Nim version 1.6.8
  • httpbeast version 0.4.1
  • Run inside docker image nimlang/nim (also tried with WSL2 and GCP debian instance)

When running and killing the binary multiple times, the program sometimes runs but most of the times it gives this error:

root@4dfa057c787d:~# ./hello
Starting 2 threads
Listening on port 8080
Traceback (most recent call last)
/root/.nimble/pkgs/httpbeast-0.4.1/httpbeast.nim(83) eventLoop
/nim/lib/system/orc.nim(46) nimIncRefCyclic
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault

I initially found the issue when using Jester. After some searching I found that I can solve the issue by commenting this block of code. Other workaround that I did was modifying Settings.loggers to ref seq[Logger], which also removes the random segfault.

httpbeast/src/httpbeast.nim

Lines 336 to 339 in 7de559e

if not isMainThread:
# We are on a new thread. Re-add the loggers from the main thread.
for logger in settings.loggers:
addHandler(logger)

Add some documentation for HTTPBeast

So that I can use it as a backend for Rosencrantz :-) I guess the API is mostly compatible with asynchttpserver, since Jester supports both, but I really have no idea

Do some profiling

It might come as a surprise to some but I haven't actually profiled httpbeast thus far. I am getting more curious whether there are some low hanging fruit there to optimise.

I'll probably do it eventually, but if someone is interested in having a crack at it, please share what you find :)

httpbeast work now.

Hi dom96,

httpbeast did not work before a little.
But work now.
Do you intend to continue developing httpbeast?
As a result of benchmark, it seems to be faster than the Web server which I made in my Nim :)

Assertion on content-length mismatch

Reproduce with

import options, asyncdispatch

import httpbeast

proc onRequest(req: Request): Future[void] =
  if req.httpMethod == some(HttpPost):
    case req.path.get()
    of "/":
      req.send("Hello World")
    else:
      req.send(Http404)

run(onRequest)

Then send a faulty HTTP request with:

curl -v -XPOST http://127.0.0.1:8080/ -d/etc/services -H "Content-Length: 12"

Results in:

httpbeast.nim(421)       run
httpbeast.nim(414)       run
httpbeast.nim(287)       eventLoop
httpbeast.nim(203)       processEvents
httpbeast.nim(140)       bodyInTransit
system.nim(3877)         failedAssertImpl
system.nim(3870)         raiseAssert
system.nim(2916)         sysFatal
Error: unhandled exception: /home/idoornekamp/.nimble/pkgs/httpbeast-0.2.1/httpbeast.nim(140, 9) `not
  trueLen < bodyLen`  [AssertionError]```

IMHO asserting or throwing on faulty remote data is not right here.

Errors with hot code reloading

I'm trying to create a dev server that benefits from hot code reloading, and I'm running into a strange issue when I import httpbeast while also compiling with --hotcodereloading:on. No project code needed to reproduce, just import httpbeast and compiling with the flag produces the following error for me:

error: ‘log__pureZlogging_107___Uq1DNQ0bb1OcS7GXX7ZrsA’ undeclared (first use in this function); did you mean ‘log__pureZlogging_107___Uq1DNQ0bb1OcS7GXX7ZrsA_actual’?
  396 |         log__pureZlogging_107___Uq1DNQ0bb1OcS7GXX7ZrsA = (tyProc__a9aA8tkMKeA4YjWsIcoguwA) hcrRegisterProc("/home/vehpeckman/.cache/nim/testserver_d/libstdlib_logging.nim.c.so", "log__pureZlogging_107___Uq1DNQ0bb1OcS7GXX7ZrsA", (void*)log__pureZlogging_107___Uq1DNQ0bb1OcS7GXX7ZrsA_actual);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |         log__pureZlogging_107___Uq1DNQ0bb1OcS7GXX7ZrsA_actual

Nim Compiler Version 1.6.6 [Linux: amd64]
Compiled at 2022-05-05

Is this a known issue? Is there any work around?

HTTP/2?

Not an issue. Just starting a discussion.
Know of any projects attempting simple HTTP/2 support? Wouldn't that be realistically faster http by fetching assets in parallel?
of course it isn't going to do anything with single simple HTML response..

Crashes with --gc:boehm

When I compiled some code with --gc:boehm and --threads:on httpbeast threw this crash:

Traceback (most recent call last)
httpbeast.nim(287)       eventLoop
httpbeast.nim(166)       processEvents
asyncdispatch.nim(1516)  poll
asyncdispatch.nim(1215)  runOnce
httpbeast.nim(261)       updateDate
times.nim(1078)          now
times.nim(1050)          local
times.nim(853)           inZone
times.nim(830)           zonedTimeFromTime
SIGSEGV: Illegal storage access. (Attempt to read from nil?)

SIGSEGV when running a basic jester 0.6.0 server in nim 2.0 on M1 Mac

I have a simple file:

import htmlgen
import jester

routes:
  get "/":
    resp h1("Hello world")

and when I compile, I get the following output:

$ nim c -r --gc:orc src/server
command line(1, 2) Warning: `gc:option` is deprecated; use `mm:option` instead [Deprecated]
Hint: used config file '/Users/steven/.choosenim/toolchains/nim-2.0.0/config/nim.cfg' [Conf]
Hint: used config file '/Users/steven/.choosenim/toolchains/nim-2.0.0/config/config.nims' [Conf]
Hint: mm: orc; threads: on; opt: none (DEBUG BUILD, `-d:release` generates faster code)
10086 lines; 0.027s; 10.484MiB peakmem; proj: /Users/steven/source/github/questdb-partition-manager/src/server; out: /Users/steven/source/github/questdb-partition-manager/src/server [SuccessX]
Hint: /Users/steven/source/github/questdb-partition-manager/src/server [Exec]
INFO Jester is making jokes at http://0.0.0.0:5000
Starting 8 threads
Listening on port 5000
Traceback (most recent call last)
/Users/steven/.nimble/pkgs/httpbeast-0.4.1/httpbeast.nim(83) eventLoop
/Users/steven/.choosenim/toolchains/nim-2.0.0/lib/system/orc.nim(46) nimIncRefCyclic
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed: '/Users/steven/source/github/questdb-partition-manager/src/server'

Nim Info

$ nim -V
Nim Compiler Version 2.0.0 [MacOSX: amd64]
Compiled at 2023-08-13
Copyright (c) 2006-2023 by Andreas Rumpf

active boot switches: -d:release

An error occurred during runtime。Error: undeclared identifier: 'EWOULDBLOCK'

PS D:\code\nim> nim c -r .\http_test.nim
Hint: used config file 'E:\tool\nim-1.2.0\config\nim.cfg' [Conf]
Hint: system [Processing]
Hint: widestrs [Processing]
Hint: io [Processing]
Hint: http_test [Processing]
Hint: options [Processing]
Hint: typetraits [Processing]
Hint: macros [Processing]
Hint: asyncdispatch [Processing]
Hint: os [Processing]
Hint: strutils [Processing]
Hint: parseutils [Processing]
Hint: math [Processing]
Hint: bitops [Processing]
Hint: algorithm [Processing]
Hint: unicode [Processing]
Hint: pathnorm [Processing]
Hint: osseps [Processing]
Hint: winlean [Processing]
Hint: dynlib [Processing]
Hint: times [Processing]
Hint: time_t [Processing]
Hint: tables [Processing]
Hint: hashes [Processing]
Hint: heapqueue [Processing]
Hint: asyncstreams [Processing]
Hint: asyncfutures [Processing]
Hint: deques [Processing]
Hint: cstrutils [Processing]
Hint: monotimes [Processing]
Hint: nativesockets [Processing]
Hint: net [Processing]
Hint: sets [Processing]
Hint: ssl_certs [Processing]
Hint: ospaths [Processing]
Hint: httpbeast [Processing]
Hint: selectors [Processing]
Hint: httpcore [Processing]
Hint: future [Processing]
Hint: underscored_calls [Processing]
Hint: logging [Processing]
Hint: osproc [Processing]
Hint: strtabs [Processing]
Hint: streams [Processing]
Hint: cpuinfo [Processing]
Hint: parser [Processing]
C:\Users\gqz.nimble\pkgs\httpbeast-0.2.2\httpbeast.nim(183, 36) Error: undeclared identifier: 'EWOULDBLOCK'

Running a simple example based on the `httpbeast 0.2.2` framework made an error.

Running a simple example based on the httpbeast 0.2.2 framework made an error :getData.headersFinished Selector not ready to send.[AssertionDefect].

Note: When i use browser request, there is no error, but when i use wrk -c100-d20--latency http://192.12.1.1:8080/ for stress test, there is an error.

My code is as follows:

import segfaults
import random

import options, asyncdispatch

import httpbeast

proc onRequest(req: Request): Future[void] {.async.} =
  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      randomize()
      let t = rand(999)
      await sleepAsync(t)
      req.send("Hello World")
    else:
      req.send(Http404)

run(onRequest)

The errors I have obtained are as follows:

Starting 6 threads
Listening on port 8080
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(287) eventLoop
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(166) processEvents
/home/lhf/Nim-devel/lib/pure/asyncdispatch.nim(1626) poll
/home/lhf/Nim-devel/lib/pure/asyncdispatch.nim(1367) runOnce
/home/lhf/Nim-devel/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/home/lhf/Nim-devel/lib/pure/asyncmacro.nim(22) onRequestNimAsyncContinue
/home/lhf/桌面/t.nim(15) onRequestIter
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(342) send
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(321) send
/home/lhf/Nim-devel/lib/system/assertions.nim(30) failedAssertImpl
/home/lhf/Nim-devel/lib/system/assertions.nim(23) raiseAssert
/home/lhf/Nim-devel/lib/system/fatal.nim(49) sysFatal
[[reraised from:
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(287) eventLoop
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(166) processEvents
/home/lhf/Nim-devel/lib/pure/asyncdispatch.nim(1626) poll
/home/lhf/Nim-devel/lib/pure/asyncdispatch.nim(1367) runOnce
/home/lhf/Nim-devel/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/home/lhf/Nim-devel/lib/pure/asyncfutures.nim(281) :anonymous
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(223) :anonymous
/home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(94) onRequestFutureComplete
]]
Error: unhandled exception: /home/lhf/.nimble/pkgs/httpbeast-0.2.2/httpbeast.nim(321, 10) `getData.headersFinished` Selector not ready to send. [AssertionDefect]

Responses never terminate (ab dies with timeout error) (OS X)

Using latest jester and Apache bench and for some reason it thinks the requests timeout. Perhaps we're not closing a socket properly or something? It's 100% reproducible here and only with httpbeast. asynchttpserver does not have the same issue.

nim c -d:release -r tests/example
...

INFO Jester is making jokes at http://0.0.0.0:5000
Starting 1 threads
ab -r -n 1000 -c 1000 http://0.0.0.0:5000/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 0.0.0.0 (be patient)
apr_pollset_poll: The timeout specified has expired (70007)

Seems to work fine in browser and with curl. AB is receiving the responses (if I used -v2), but obviously something else is off.

Thoughts?

Index out of bound on sending incomplete request method

nim c -r tests/helloworld.nim
printf "G" | nc localhost 8080

Fails with

Listening on port 8080
/home/grfork/reps/httpbeast/src/httpbeast.nim(296) eventLoop
/home/grfork/reps/httpbeast/src/httpbeast.nim(203) processEvents
/home/grfork/reps/httpbeast/src/httpbeast.nim(115) slowHeadersCheck
/home/grfork/reps/httpbeast/src/httpbeast/parser.nim(10) parseHttpMethod
/home/grfork/.choosenim/toolchains/nim-1.4.0/lib/system/fatal.nim(49) sysFatal
Error: unhandled exception: index 1 not in 0 .. 0 [IndexDefect]
Error: execution of an external program failed: '/home/grfork/reps/httpbeast/tests/helloworld '

Unsanitized headers can break body in response

I was creating some custom headers to add to my response, and was using IDE magic to replicate some lines, which resulted in my last header arguement still containing trailing \c\L chars.

My initial presumption was this would be appropriately handled by the library, but it was not, and caused my resulting response body to be missing characters as the \c\L essentially caused overflow into where the body was expected to start.

An easy solution on my end was

 let sanitizedHeaders = HEADERS.strip(leading = false, chars = {'\c', '\L'})

before inserting the headers in send

I am wondering if you're of the opinion that the library should in fact handle this, or if it's up to the user to sanitize. I understand this is supposed to be performance-oriented and such finer details may be left out for performance reasons, however, it also exposes an unsafeSend and send method, and when such a distinction occurs, I would presume it's pretty hard to shoot myself in the foot with send, yet I accomplished it just via adding some basic headers.

Another option which would lessen the impact on send (instead of doing a check/strip on each call) would be a method exposed to the user to create a HttpBeast safe and compatible header string.

Error: type mismatch: got <nil> but expected 'string'

Get an error when trying to compile with Jester.

/home/flairfocus/.nimble/pkgs/httpbeast-0.2.0/httpbeast.nim(58, 44) Error: type mismatch: got <nil> but expected 'string'

Nim Info

$ nim -v
Nim Compiler Version 0.18.1 [Linux: amd64]
Compiled at 2018-08-18
Copyright (c) 2006-2018 by Andreas Rumpf

active boot switches: -d:release

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.