Skip to content

Commit

Permalink
alternative
Browse files Browse the repository at this point in the history
  • Loading branch information
guzba committed Feb 5, 2024
1 parent 11b56ee commit ef3b2e5
Show file tree
Hide file tree
Showing 17 changed files with 100 additions and 206 deletions.
2 changes: 1 addition & 1 deletion examples/advanced_websockets.nim
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var
initLock(lock)

# This is the HTTP handler for /* requests. These requests are upgraded to websockets.
proc upgradeHandler(request: RoutedRequest) =
proc upgradeHandler(request: Request) =
let channel = request.uri[1 .. ^1] # Everything after / is the channel name.

# We need to take the lock on global memory, upgrade to websocket and store
Expand Down
4 changes: 2 additions & 2 deletions examples/basic_database.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ db.withConnection conn:
conn.exec(sql"insert or replace into table1 values (0, 0)")

# A request to /get will return the count
proc getHandler(request: RoutedRequest) =
proc getHandler(request: Request) =
var count: int
db.withConnection conn:
count = parseInt(conn.getValue(sql"select count from table1 limit 1"))
Expand All @@ -24,7 +24,7 @@ proc getHandler(request: RoutedRequest) =
request.respond(200, headers, "Count: " & $count & "\n")

# A request to /inc will increase the count by 1
proc incHandler(request: RoutedRequest) =
proc incHandler(request: Request) =
db.withConnection conn:
conn.exec(sql"update table1 set count = count + 1")

Expand Down
2 changes: 1 addition & 1 deletion examples/basic_multipart.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import mummy, mummy/routers, mummy/multipart
## curl -v -F key1="abc" -F key2="def" http://localhost:8080/multipart
## curl -v -F upload=@<FILE_PATH> http://localhost:8080/multipart

proc multipartHandler(request: RoutedRequest) =
proc multipartHandler(request: Request) =
let multipartEntries = request.decodeMultipart()
for entry in multipartEntries:
echo entry.name, ", has data? ", entry.data.isSome
Expand Down
4 changes: 2 additions & 2 deletions examples/basic_redirect.nim
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import mummy, mummy/routers

proc firstHandler(request: RoutedRequest) =
proc firstHandler(request: Request) =
# Responds with a 302 redirect
request.respond(302, @[("Location", "/second")])

proc secondHandler(request: RoutedRequest) =
proc secondHandler(request: Request) =
request.respond(200, @[("Content-Type", "text/plain")], "Hello, World!")

var router: Router
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_router.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import mummy, mummy/routers

proc indexHandler(request: RoutedRequest) =
proc indexHandler(request: Request) =
var headers: HttpHeaders
headers["Content-Type"] = "text/plain"
request.respond(200, headers, "Hello, World!")
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_router_path_params.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import mummy, mummy/routers

## http://localhost:8080/objects/abc

proc objectsHandler(request: RoutedRequest) =
proc objectsHandler(request: Request) =
var headers: HttpHeaders
headers["Content-Type"] = "text/plain"
request.respond(200, headers, "Object: " & request.pathParams["id"])
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_router_query_params.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import mummy, mummy/routers

## http://localhost:8080/search?name=foo

proc searchHandler(request: RoutedRequest) =
proc searchHandler(request: Request) =
var headers: HttpHeaders
headers["Content-Type"] = "text/plain"
request.respond(200, headers, "Name: " & request.queryParams["name"])
Expand Down
4 changes: 2 additions & 2 deletions examples/basic_websockets.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import mummy, mummy/routers

proc indexHandler(request: RoutedRequest) =
proc indexHandler(request: Request) =
var headers: HttpHeaders
headers["Content-Type"] = "text/html"
request.respond(200, headers, """
Expand All @@ -12,7 +12,7 @@ proc indexHandler(request: RoutedRequest) =
</script>
""")

proc upgradeHandler(request: RoutedRequest) =
proc upgradeHandler(request: Request) =
let websocket = request.upgradeToWebSocket()
websocket.send("Hello world from WebSocket!")

Expand Down
4 changes: 2 additions & 2 deletions examples/chat.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var

initLock(lock)

proc indexHandler(request: RoutedRequest) =
proc indexHandler(request: Request) =
var headers: HttpHeaders
headers["Content-Type"] = "text/html"
request.respond(200, headers, """
Expand All @@ -43,7 +43,7 @@ proc indexHandler(request: RoutedRequest) =
<div>Messages received:</div>
""")

proc upgradeHandler(request: RoutedRequest) =
proc upgradeHandler(request: Request) =
let websocket = request.upgradeToWebSocket()
websocket.send("Hello from WebSocket server!")

Expand Down
2 changes: 1 addition & 1 deletion examples/client_headers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import mummy, mummy/routers

## This example shows how to access client request headers.

proc indexHandler(request: RoutedRequest) =
proc indexHandler(request: Request) =
# Access specific request headers
echo request.headers["Host"]

Expand Down
16 changes: 8 additions & 8 deletions examples/custom_handler_params.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ import mummy, mummy/routers
## We can then create any number of handlers using the AuthenticatedHandler
## signature and have them all go through one authentication code path.

type AuthenticatedHandler = proc(request: RoutedRequest, userId: string) {.gcsafe.}
type AuthenticatedHandler = proc(request: Request, userId: string) {.gcsafe.}

proc indexHandler(request: RoutedRequest) =
proc indexHandler(request: Request) =
request.respond(200, @[("Content-Type", "text/plain")], "Hello, World!")

proc profileHandler(request: RoutedRequest, userId: string) =
proc profileHandler(request: Request, userId: string) =
# This is the authenticated endpoint for a user's profile.
request.respond(200, @[("Content-Type", "text/plain")], "Hello " & userId)

proc settingsHandler(request: RoutedRequest, userId: string) =
proc settingsHandler(request: Request, userId: string) =
# This is the authenticated endpoint for a user's settings.
request.respond(200, @[("Content-Type", "text/plain")], "Settings for " & userId)

proc toHandler(wrapped: AuthenticatedHandler): RouteHandler =
# Calling `toHandler` returns a simple RouteHandler proc for an
proc toHandler(wrapped: AuthenticatedHandler): RequestHandler =
# Calling `toHandler` returns a simple RequestHandler proc for an
# AuthenticatedHandler so it can be registered with a Router.
return proc(request: RoutedRequest) =
return proc(request: Request) =
# This code runs before we call the AuthenticatedHandler.
# We can do the user authentication that all AuthenticatedHandlers
# expect here.
Expand All @@ -53,4 +53,4 @@ server.serve(Port(8080))
##
## You can add any number of parameters of any type you want to a custom
## handler type and then use the example above for how to wrap it in a
## simple RouteHandler.
## simple RequestHandler.
37 changes: 27 additions & 10 deletions src/mummy.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ when not compileOption("threads"):
import mummy/common, mummy/internal, std/atomics, std/base64,
std/cpuinfo, std/deques, std/hashes, std/nativesockets, std/os,
std/parseutils, std/random, std/selectors, std/sets, crunchy, std/strutils,
std/tables, std/times, webby/httpheaders, zippy, std/options
std/tables, std/times, webby/httpheaders, webby/queryparams, webby/urls,
zippy, std/options

when defined(linux):
when defined(nimdoc):
Expand All @@ -22,7 +23,7 @@ when defined(linux):

import std/locks

export Port, common, httpheaders
export Port, common, httpheaders, queryparams

const
listenBacklogLen = 128
Expand All @@ -35,12 +36,15 @@ let

type
RequestObj* = object
httpVersion*: HttpVersion
httpMethod*: string
uri*: string
headers*: HttpHeaders
body*: string
remoteAddress*: string
httpVersion*: HttpVersion ## HTTP version from the request line.
httpMethod*: string ## HTTP method from the request line.
uri*: string ## Raw URI from the HTTP request line.
path*: string ## Decoded request URI path.
queryParams*: QueryParams ## Decoded request query parameter key-value pairs.
pathParams*: PathParams ## Router named path parameters key-value pairs.
headers*: HttpHeaders ## HTTP headers key-value pairs.
body*: string ## Request body.
remoteAddress*: string ## Network address of the request sender.
server: Server
clientSocket: SocketHandle
clientId: uint64
Expand Down Expand Up @@ -124,11 +128,15 @@ type
requestCounter: int # Incoming request incs, outgoing response decs

IncomingRequestState = object
headersParsed, chunked: bool
headersParsed: bool
chunked: bool
loggedUnexpectedData: bool
contentLength: int
httpVersion: HttpVersion
httpMethod, uri: string
httpMethod: string
uri: string
path: string
queryParams: QueryParams
headers: HttpHeaders
body: string

Expand Down Expand Up @@ -734,6 +742,8 @@ proc popRequest(
result.httpVersion = dataEntry.requestState.httpVersion
result.httpMethod = move dataEntry.requestState.httpMethod
result.uri = move dataEntry.requestState.uri
result.path = move dataEntry.requestState.path
result.queryParams = move dataEntry.requestState.queryParams
result.headers = move dataEntry.requestState.headers
result.body = move dataEntry.requestState.body
result.body.setLen(dataEntry.requestState.contentLength)
Expand Down Expand Up @@ -810,6 +820,13 @@ proc afterRecvHttp(
if space2 == -1:
return true # Invalid request line, close the connection
dataEntry.requestState.uri = dataEntry.recvBuf[space1 + 1 ..< space2]
try:
var url = parseUrl(dataEntry.requestState.uri)
dataEntry.requestState.path = move url.path
dataEntry.requestState.queryParams = move url.query
except:
server.log(DebugLevel, "Dropped connection, invalid request URI")
return true # Invalid request URI, close the connection
if dataEntry.recvBuf.find(
' ',
space2 + 1,
Expand Down
33 changes: 33 additions & 0 deletions src/mummy/common.nim
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import std/typetraits

type
MummyError* = object of CatchableError

Expand All @@ -9,6 +11,37 @@ type

LogHandler* = proc(level: LogLevel, args: varargs[string]) {.gcsafe.}

PathParams* = distinct seq[(string, string)]

converter toBase*(pathParams: var PathParams): var seq[(string, string)] =
pathParams.distinctBase

converter toBase*(pathParams: PathParams): lent seq[(string, string)] =
pathParams.distinctBase

proc `[]`*(pathParams: PathParams, key: string): string =
## Returns the value for key, or an empty string if the key is not present.
for (k, v) in pathParams.toBase:
if k == key:
return v

proc `[]=`*(pathParams: var PathParams, key, value: string) =
## Sets the value for the key. If the key is not present, this
## appends a new key-value pair to the end.
for pair in pathParams.mitems:
if pair[0] == key:
pair[1] = value
return
pathParams.add((key, value))

proc contains*(pathParams: PathParams, key: string): bool =
for pair in pathParams:
if pair[0] == key:
return true

proc getOrDefault*(pathParams: PathParams, key, default: string): string =
if key in pathParams: pathParams[key] else: default

proc echoLogger*(level: LogLevel, args: varargs[string]) =
## This is a simple echo logger.
if args.len == 1:
Expand Down
Loading

0 comments on commit ef3b2e5

Please sign in to comment.