Web++: A C++ Web Framework
This is a valid WHATWG URL with a hidden "Hello World" binary message encoded within: - / is 1 - \ is 0 https:/\\//\\////\\\\//\\/\\/\\\\/\\\\///\\\\/\\\\///\\\\/\\\\\\\\//\\//////\\/\\/\\\\\\/\\\\/\\\\\\\\/\\\\\\//\\//\\\\/\\\\///\\\\//\\//example.com
Here's a vibe coded script that with it you can hide any message in a valid URL using forward and backward slashes.
Just to show you how poorly WHATWG URL Specs really are written.
The script
Just to show you how poorly WHATWG URL Specs really are written.
The script
Locally Bound Globals
I'm not sure if there's a another name for it or not, but it's essentially a "global pointer" that acts like a reference, and can be made to point to local variables, so you don't have to keep passing it as args to funcs you call.
Using lbg_scope, we can assign the global pointer to point to local variable, and when it goes out of scope, it'll point to whatever was pointer before this scope.
Thus creating a very easy transition.
The global pointer's type itself can easily be implemented by just using "self" to point to the local instance of itself.
This is good for when you don't want to keep passing arguments 5 layers deep into your call chain, and just want to have access to it.
The Code
I'm not sure if there's a another name for it or not, but it's essentially a "global pointer" that acts like a reference, and can be made to point to local variables, so you don't have to keep passing it as args to funcs you call.
Using lbg_scope, we can assign the global pointer to point to local variable, and when it goes out of scope, it'll point to whatever was pointer before this scope.
Thus creating a very easy transition.
The global pointer's type itself can easily be implemented by just using "self" to point to the local instance of itself.
This is good for when you don't want to keep passing arguments 5 layers deep into your call chain, and just want to have access to it.
The Code
Web++: A C++ Web Framework
Locally Bound Globals I'm not sure if there's a another name for it or not, but it's essentially a "global pointer" that acts like a reference, and can be made to point to local variables, so you don't have to keep passing it as args to funcs you call. Usingβ¦
Turns out this is called Dynamic Scoping thanks to Paul Snively for telling me.
Pros:
- No need to pass tons of arguments to functions, or create unnecessary closures, for dependency injection.
- Saves some typing
- Simplifies function signatures
Cons:
- There's a small performance penalty due to indirect access, and compilers not being able to optimize things away.
- There's no way to know what dependencies a function needs, so we have to make them all available; but for things like "logging" it's not a problem for webpp, but for "request", and "response" it adds a "pointer exchange" performance penalty on each request which is not that much to worry about.
- Switching scopes can be hidden from users, but that is not really a problem for web++.
- With dynamic scoping, variables would still be available out of scope, so they would be kept in memory for longer in some cases, but that's the intended behavior anyway, at least in Web++.
How to have both options in Web++?
So, for Web++, I think it's best for when the user writes the signature of their route functions to include request/response/context, then we just don't bother dynamic scope, but for the function signatures that don't return anything, and don't accept request/response/context, we provide the dynamic scopes for them because that would be the only way the user would be able to fill the response of the request anyway.
Any objections or questions about this before I go and implement this in the library?
Pros:
- No need to pass tons of arguments to functions, or create unnecessary closures, for dependency injection.
- Saves some typing
- Simplifies function signatures
Cons:
- There's a small performance penalty due to indirect access, and compilers not being able to optimize things away.
- There's no way to know what dependencies a function needs, so we have to make them all available; but for things like "logging" it's not a problem for webpp, but for "request", and "response" it adds a "pointer exchange" performance penalty on each request which is not that much to worry about.
- Switching scopes can be hidden from users, but that is not really a problem for web++.
- With dynamic scoping, variables would still be available out of scope, so they would be kept in memory for longer in some cases, but that's the intended behavior anyway, at least in Web++.
How to have both options in Web++?
So, for Web++, I think it's best for when the user writes the signature of their route functions to include request/response/context, then we just don't bother dynamic scope, but for the function signatures that don't return anything, and don't accept request/response/context, we provide the dynamic scopes for them because that would be the only way the user would be able to fill the response of the request anyway.
Any objections or questions about this before I go and implement this in the library?
π1
Null === Empty
Well, at least we should pretend they are in standards, specs, and what not.
For example, WHATWG URL Specs should not rely on nullness of host to carry algorithmic information.
These should be implementation details.
Well, at least we should pretend they are in standards, specs, and what not.
For example, WHATWG URL Specs should not rely on nullness of host to carry algorithmic information.
These should be implementation details.
Only 396/11463 assertions are now failing in the standard WHATWG URL tests.
I've re-designed some parsers.
I've re-designed some parsers.
π₯1
Also, I've implemented Middlewares which was kinda a big deal. I've been through so many designs of the middlewares already; hope this one would be the one to stay.
The middlewares is designed in quite an interesting way. I'll write about it some day more.
The middlewares is designed in quite an interesting way. I'll write about it some day more.
β€3
I've also re-designed header fields as well; there's still one small detail that depends on me figuring out the headers themselves which requires re-aligning the request and responses to match the new middleware solution, which requires figuring out the design of the underlying protocol and who owns the responses and requests and where and who gets to modify them and also figure out where to put mini-caches and the relationship of the cache and the middlewares, and also how the I/O and async play into all of this.
I'm probably one or two pieces of the puzzle away from putting them all together and going with the implementations of the HTTP/FastCGI/CGI/... .
I'm probably one or two pieces of the puzzle away from putting them all together and going with the implementations of the HTTP/FastCGI/CGI/... .
β€2
I also have some parts of the HTTP/1 parser as well. I'm probably gonna use openssl for now for the lower levels, but I'm gonna design it in a way that we can switch out the protocol easily later on.
β€2
Table-Driven Character Classification (Lookup Table/LUT) and Bitmask Accumulation
One of the ways that I'm designing my parsers is using Bitmask Accumulation in combination with a table lookup.
Essentially, I create a table of 256 length and lookup each character in it and use bitwise operator
This is a form of classification or categorization.
Then we simply can ask our 8 bit integer if the input for example for the code in the picture:
- Is it possible for it to have IPv4
- Possibility of IPv6
- Contains any specific characters that need special handling like % # ? / \
- Any invalid code points
- Possibility of existence of punycodes
- Existence of Unicode
Of course depending on the table, we can find out other information as well.
This helps the algorithm to completely disable a whole lot of checks.
One of the ways that I'm designing my parsers is using Bitmask Accumulation in combination with a table lookup.
Essentially, I create a table of 256 length and lookup each character in it and use bitwise operator
or (|) to accumulate them all into one single uint8_t integer which each bit of that integer can convey information about the types of characters that gave it.This is a form of classification or categorization.
Then we simply can ask our 8 bit integer if the input for example for the code in the picture:
- Is it possible for it to have IPv4
- Possibility of IPv6
- Contains any specific characters that need special handling like % # ? / \
- Any invalid code points
- Possibility of existence of punycodes
- Existence of Unicode
Of course depending on the table, we can find out other information as well.
This helps the algorithm to completely disable a whole lot of checks.
π1
Instantiating a generator as a template for certain reasons prevents compilers such as GCC and Clang from identifying it as a coroutine, resulting in errors indicating that
std::coroutine_traits was not found. Implementing a non-templated override resolves this issue. It is uncertain whether this constitutes a compiler bug or standard behavior.
Web++: A C++ Web Framework
I wanted to use std::indirect, but I needed something pimpl idiom that: - consteval constructible-only - consteval copyable-only Ended up writing custom nullable_indirect; at least for now.
Called it
consteval_copyable and removed a whole bunch of repetitive constructors.π1
Let's hope introducing "dynamic scopes" to a highly templated and complex pipeline-builder "context" type is not gonna backfire.
The user-facing pipeline API will look cute though.
The user-facing pipeline API will look cute though.
π1π1