Added more docs

This commit is contained in:
Toby Chui 2025-05-26 22:03:07 +08:00
parent c56e317bfd
commit a85bf82c3e
19 changed files with 568 additions and 206 deletions

View File

@ -265,7 +265,9 @@ func mdToHTML(md []byte) []byte {
// create HTML renderer with extensions
htmlFlags := html.CommonFlags | html.HrefTargetBlank
opts := html.RendererOptions{Flags: htmlFlags}
opts := html.RendererOptions{
Flags: htmlFlags,
}
renderer := html.NewRenderer(opts)
return markdown.Render(doc, renderer)

View File

@ -38,46 +38,6 @@ func optimizeCss(htmlContent []byte) ([]byte, error) {
//Replace the img with ts-image
originalHTMLContent = strings.Replace(originalHTMLContent, orginalParent, "<div class=\"ts-image is-rounded\" style=\"max-width: 800px\">"+orginalParent+"</div>", 1)
})
/*
// Replace h* elements
doc.Find("h1").Each(func(i int, s *goquery.Selection) {
originalHeader, err := s.Html()
if err != nil {
fmt.Println("Error getting header HTML:", err)
return
}
// Patch the bug in the parser that converts " />" to "/>"
originalHeader = strings.ReplaceAll(originalHeader, "/>", " />")
wrappedHeader := fmt.Sprintf("<div class=\"ts-header is-big\">%s</div>", originalHeader)
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, s.Text(), wrappedHeader)
})
*/
doc.Find("h2").Each(func(i int, s *goquery.Selection) {
originalHeader, err := s.Html()
if err != nil {
fmt.Println("Error getting header HTML:", err)
return
}
// Patch the bug in the parser that converts " />" to "/>"
originalHeader = strings.ReplaceAll(originalHeader, "/>", " />")
wrappedHeader := fmt.Sprintf("<div class=\"ts-header is-large\">%s</div>", originalHeader)
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, s.Text(), wrappedHeader)
})
doc.Find("h3").Each(func(i int, s *goquery.Selection) {
originalHeader, err := s.Html()
if err != nil {
fmt.Println("Error getting header HTML:", err)
return
}
// Patch the bug in the parser that converts " />" to "/>"
originalHeader = strings.ReplaceAll(originalHeader, "/>", " />")
wrappedHeader := fmt.Sprintf("<div class=\"ts-header\">%s</div>", originalHeader)
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, s.Text(), wrappedHeader)
})
// Add "ts-text" class to each p element
doc.Find("p").Each(func(i int, s *goquery.Selection) {
@ -103,7 +63,7 @@ func optimizeCss(htmlContent []byte) ([]byte, error) {
}
// Replace <hr> with <div class="ts-divider"></div>
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "<hr>", "<div class=\"ts-divider\"></div>")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "<hr>", "<div class=\"ts-divider has-top-spaced-large\"></div>")
})
// Add ts-table to all table elements
@ -120,6 +80,43 @@ func optimizeCss(htmlContent []byte) ([]byte, error) {
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, originalTable, fmt.Sprintf("<table class=\"%s\">%s</table>", newClass, originalTable))
})
// Replace <ul> <ol> and <li>
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "<ul>", "<div class=\"ts-list is-unordered\">")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "</ul>", "</div>")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "<ol>", "<div class=\"ts-list is-ordered\">")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "</ol>", "</div>")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "<li>", "<div class=\"item\">")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "</li>", "</div>")
// Replace <strong> with <span class="ts-text is-heavy"></span>
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "<strong>", "<span class=\"ts-text is-heavy\">")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "</strong>", "</span>")
// Replace <code> without class with <span class="ts-text is-code">
for {
startIndex := strings.Index(originalHTMLContent, "<code>")
if startIndex == -1 {
break
}
endIndex := strings.Index(originalHTMLContent[startIndex+6:], "</code>")
if endIndex == -1 {
break
}
endIndex += startIndex + 6
codeSegment := originalHTMLContent[startIndex : endIndex+7] // Include </code>
fmt.Println(">>>>", codeSegment)
if !strings.Contains(codeSegment, "class=") {
replacement := strings.Replace(codeSegment, "<code>", "<span class=\"ts-text is-code\">", 1)
replacement = strings.Replace(replacement, "</code>", "</span>", 1)
originalHTMLContent = strings.Replace(originalHTMLContent, codeSegment, replacement, 1)
} else {
// Skip if <code> already has a class
break
}
}
//Replace blockquote to <div class="ts-quote">
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "<blockquote>", "<div class=\"ts-quote\">")
originalHTMLContent = strings.ReplaceAll(originalHTMLContent, "</blockquote>", "</div>")

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2025-05-26T13:15:22.444Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.4.2 Chrome/78.0.3904.130 Electron/7.1.4 Safari/537.36" etag="PlPASUA5DkVflfK71JS_" version="12.4.2" type="device" pages="1"><diagram id="fpBU8wsgIdeXEMTqvbYu" name="Page-1">5VrRcps4FP0az2Qf1gMIAX5MnCbNbHcn00xn277syCBjpRhRIcdOvn4lkG2Q1CZxMSGpHzxwBZI49xxd3QsjMF1uLhkqFn/TBGcjz0k2I3A+8rzQn4h/abivDRAGtSFlJKlN7t5wQx6wMjrKuiIJLlsXckozToq2MaZ5jmPesiHG6Lp92Zxm7VELlGLDcBOjzLT+SxK+qK2RF+7t7zFJF9uR3UA98BJtL1ZPUi5QQtcNE3g3AlNGKa+PlpspziR2W1zq+y5+0LqbGMM5f8oNX+6Xk9ldzsLJZXobx5+vspvwT9XLHcpW6oG/UoY292rK/H6LA6OrPMGyK2cEztYLwvFNgWLZuhaOF7YFX2bizBWH5tS242DG8aZhUlO9xHSJORPDOtvWiYJN8caN1Pl67wV3C+2i4QFf2ZByfLrreo+NOFDwPAMqz4DqOlulJH9xqHxvaFABAyoDJJwnp1Ke4izOUFmSuI0L3hD+WUI4hursS6PlfKPQrU7u1UmCykWF+0+BLemKxfhxTeCktTKY8DfghRZ0tzaGM8TJXXs9sUGuRrimRMx4510Qtr3ru5rX6udRdzX1r3W0m5DqKPC0jjhiKeZGRxUDdo99OCn8V02KYFCk8MPJGEbO/ue3XCtCwbjR6LiHMUaM8qKMcYPHKdP3Yuv5sA01sCy2FseD4FiLrRv9urB+CMxA+O4Fjh3151Na6wj2S+kBMtoPoR2SRxh9PEJPhhwpaoK8rkgRTva/txgotsM1GHOVc0bLQqaEOneELnibLSgjaS6pJJyNmTBI9RCRBJ6qhiVJEnn7GcMleUCzqivJmUI+UvWQ8GwEzyVxRHcrTss6k5W9M8qFB6nsJ+hGskDb8e9yzAanQgundLd0lxuZydGbi0F66ADOoRtxLZgBfUd/bLWY6dlNTuZzkqcS7TwpdiMPRDYioexGNy5o6yYKLKGuV+E8ISvqf0PQRgn4lg0BtIB0vNKLuW3C43Q8km65KCV3X5ytnazq7Y3Ybl1o4B70Ss7QgH2aEWxZG8oFKuThapmdxpw20f2AZji7FlApLc8o53QpLshkwxmKv6UVwac0k/eJ3sC8+lk8xKnGdrriGcnxdFf8dTqSgFZzsSR5oM8tsfcb5HhawdfXV5Mnh1fdeT2neJ6ZvnwqSs4wWr740h44bWwGUCo2t+7PZvbxkr2BSCOAPSRxPx+k75QOmG+mRD9EqGUzrlz/BgK+C7SV6qXTOPCEFze9L1nQa7PdVnC17EaPV3AFv0Gy68H2XtTX0Ty04OpHPa8jZoJ1QdkasaQC6XsFH0rE+iA/LBhSRtHIf2FH7y6idmLn2yq9vWYYoINS79ClZChgcujGVqsbQb0AdewXuGZIPkFxjAs5x3ktqj+GJJvOykaOthiGPZaN/rq6/e/7x3+uPn0oNmw9+fhwPvvaxQcWc5JljZw7gThKfGEXmQr9hhstkTcDwb5wranKAu1QYhbs6uWGJjs9F+pOdlZfmwHs5EpEJ2f79ZGMYitcyhkvUJ5kJE8HpcJ98DIU9yTy7IKX5l1LdexowcvqGNi5COdRjOPYJsJZBGW0fg0iDMeaow4t5ATaB0vweFtHq3/NovMJ0YR3W71fHKbugm505080d9o2jTZCHaA7cbr/HrZ24/6jYvDufw==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2025-05-26T13:04:13.815Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.4.2 Chrome/78.0.3904.130 Electron/7.1.4 Safari/537.36" etag="gzmRxnqbWDJ6DOZzUBFr" version="12.4.2" type="device" pages="1"><diagram id="fpBU8wsgIdeXEMTqvbYu" name="Page-1">5VrRjqM2FP2aSNOHRoAxhMeZzO52pW010qjqbt884AFawMg4k2S/vjaYALa3k0kDpJk8RHABY849x9fHsADrfPeJojL5lUQ4WzhWtFuA+4XjBBbk/yKwbwKe7zSBmKZRE7K7wGP6HcugJaObNMLV4ERGSMbSchgMSVHgkA1iiFKyHZ72TLLhXUsUYy3wGKJMj/6RRixpoivH7+K/4DRO2jvbXtAcyVF7snySKkER2fZC4MMCrCkhrNnKd2ucCexaXJrrPv7g6KFjFBfsmAu+7fPg6aWgfvAp/isMv37OHv2fZSsvKNvIB/6TULTbyy6zfYsDJZsiwqIpawHutknK8GOJQnF0yxPPYwnLM75n803ZKKYM737YW/uAAecOJjlmlN/Wai8IJGySN/ZK7m+7LNgttEkvA66MIZn4+NB0hw3fkPC8ASpHg+oh28RpMTtUrnNpUAENKg0kXES3Qp58L8xQVaXhEBe8S9lXAeESyr1vvSP3O4luvbOXOxGqkhr3DlgcafJWYOW9Ihsa4tdEosPfgxca0G1jFGeIpS/Dbpggl3d4ICnv4CG7wB9m17WVrDXdl1f19a80dOhQOxI7SkMM0RgzraGaAYfHPp0U7nWRwpuTFK4fLOHK6n7uILW8FCx7By37NMbwu8zKGNt7nTJTD7aOC4dQA8Nga0g88MYabO3VfxfWsbqYi++OZ5lRfzullYbgtJS+QEa7PjRD8gqjxyN08L+qFA1jLrpS+EH3u8ZC0d6ux5jPBaOkKoUlVLnDpcKGbEFZGheCSjy3mPKAEFTKTeCtPJCnUSQuv6O4Sr+jp7opwZlSPFL9kPBuAe8FcXhzG0aqxsmK1ilhPINEtOOdR7JAmfEfPGaPU76BU2pazueNdHN0dTVILR3AOnUirhQzoM7ox1aLbs++pJXoIHmuU8ZRCvlGiEq2objOAUuqS9IRd5jnmc2tVkMhrTy99pk4NZ6SjrBJ088QwJCwrmGGAA0gjbcWo8+j8DJeLkRaPqIoN6zKTM3WEYb5w0Ax2zDva7CvsxQXeo2tElSKzU2e3YaM9NH9gp5w9sChklp+IoyRnJ+QiQN3KPw7rgm+Jpm4jrcGnuufIUOMKGwnG5alBV4fVoOtM0lAWYQxuD4w5RzZeQemT1kBdtXR5Oh6qyZvYs/n6H7m97JiFKN89qHds4bYXMDasT6XfzOzJ3N/c0nDgxO4un+/ydQeD+ivqng7KVfLblmn/goKvg2UkWpuXweOeJMz+ZAFnSHbTSuwhtnoeCuw4B24XwcOVwldFc2jRxXF/brBxOOIbrBufiOig8I0dO5XeN6fZh9Uep4XnslUwNUSDP2ca1rxNS35jjfOwOtXUDAUEFTBPH46qzSkvgsZW0Cmdxit6b62gmwrWFuzK0W34FenFLXWwFOdn7pkC9WiNbZUdJd+c+FVZqQ3FNAwSQsm1c05Xyr2TKW19FuPafaVx4rtiE9OmrI9n+dUUnpyAVNmgFB9kTL2h0i6k7zhqilJUeH5tXgO8akrZ4ZFnUltpPsODJJWa/xzyWM1sTx0z39l8nCGL/1gO904vzz4bveBd5Og7it58OEf</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

View File

@ -2,16 +2,26 @@
As you can see in the Introspect section, there are two types of capture mode in Zoraxy plugin API.
- Static Capture Mode
- Dynamic Capture Mode
**Notes: When this document mention the term "endpoint", it means a particular sub-path on the plugin side. For example `/capture` or `/sniff`. In actual implementation, this can be a `http.HandleFunc` or `http.Handle` depends on the plugin implementation.**
---
## Static Capture Mode
Static Capture Mode register a static path to Zoraxy, when the plugin is enalbed on a certain HTTP proxy rule, all request that matches the static capture registered paths are forwarded to the plugin without asking first.
Static Capture Mode register a static path to Zoraxy, when the plugin is enabled on a certain HTTP proxy rule, all request that matches the static capture registered paths are forwarded to the plugin without asking first. The overall process is shown in the diagram below.
(To be added)
![static_capture](img/4. Capture Modes/static_capture.png)
The main benefit of static capture mode is that the capture paths are stored in radix tree. That means it takes O(logn) time to resolve the path and forward the request. Hence, **this mode is generally faster** if your plugin always listens to a few certain paths for extended functionalities.
---
## Dynamic Capture Mode
@ -20,4 +30,29 @@ Dynamic Capture Mode register two endpoints to Zoraxy.
1. DynamicCaptureSniff - The sniffing endpoint where Zoraxy will first ask if the plugin want to handle this request
2. DynamicCaptureIngress - The handling endpoint, where if the plugin reply the sniffing with "YES", Zoraxy forward the incoming request to this plugin at this defined endpoint.
(To be added)
The whole process will takes a few request exchange between plugin and Zoraxy core. Since both of them are communicating via the loopback interface, speed should not be too big of a concern here.
The request handling flow is shown in the diagram below.
![dynamic_capture](img/4. Capture Modes/dynamic_capture.png)
Once Zoraxy receive a request from a client that matches one of the HTTP Proxy Rule, Zoraxy will forward the request header to all the plugins that matches the following criteria
1. The plugin is assigned to a tag that is currently attached to the given HTTP Proxy that the request is coming through
2. The plugin is enabled and running
3. The plugin has registered its dynamic capture sniffing endpoint in Introspect
Then the plugin `/sniff` endpoint will receive some basic header information about the request, and response with `SniffResultAccpet` or `SniffResultSkip` to accept or reject handling such request. The response are defined in `zoraxy_plugin` as a public type where you can access with `zoraxy_plugin.SniffresultAccept` and `zoraxy_plugin.SniffResultSkip` respectively.
Note that this shall only be used if static capture mode cannot satisfy your needs in implementation the feature you want, as **dynamic capture is way slower than static capture mode**.
---
## Mixing Capture Modes
It is possible for you to mix both Static and Capture modes if that is what you want. A few thing you need to know about mixing both mode in single plugin
1. Static capture mode has higher priority than dynamic capture mode across all plugins. That means if you have a request that matches Plugin A's static capture path and Plugin B's dynamic capture, the request will be first handled by Plugin A
2. The same plugin can register both static and dynamic capture modes. Similar to item (1), if the request has already captured by your static capture path, Zoraxy will not proceed and forward the request header to your dynamic sniffing endpoint.
3. In case there is a collision in static capture paths between two plugins, the longest one will have priority. For example, if Plugin A registered `/foo` and Plugin B registered `/foo/bar`, when a request to `/foo/bar/teacat` enter Zoraxy, Plugin B is used for handling such request.

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

View File

@ -110,6 +110,9 @@
<a class="item" href="/html/2. Architecture/3. Configure.html">
Configure
</a>
<a class="item" href="/html/2. Architecture/4. Capture Modes.html">
Capture Modes
</a>
</div>
<a class="item">
Getting Started
@ -135,9 +138,9 @@
<p>
<p class="ts-text">
Zoraxy plugins are distributed as binaries, and developers have the flexibility to choose whether to open source them or not
<strong>
<span class="ts-text is-heavy">
as the plugin library and interface are open source under the LGPL license
</strong>
</span>
.
</p>
</p>
@ -146,74 +149,74 @@
There are two primary types of plugins:
</p>
</p>
<ul>
<li>
<strong>
<div class="ts-list is-unordered">
<div class="item">
<span class="ts-text is-heavy">
Router plugins
</strong>
</span>
: Involved with connections from HTTP proxy rules.
<br>
</li>
<li>
<strong>
</div>
<div class="item">
<span class="ts-text is-heavy">
Utility plugins
</strong>
</span>
: Provide user interfaces for various network features that operate independently of the Zoraxy core.
<br>
</li>
</ul>
<div class="ts-divider"></div>
</div>
</div>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="how-plugins-are-distributed-installed">
How plugins are distributed &amp; installed
</h2>
<p>
<p class="ts-text">
Zoraxy plugins are distributed as platform-dependent binaries, tailored to specific operating systems and CPU architectures. These binaries follow a naming convention that includes the operating system, CPU architecture, and plugin name, such as
<code>
<span class="ts-text is-code">
linux_amd64_foobar
</code>
</span>
,
<code>
<span class="ts-text is-code">
windows_amd64_foobar.exe
</code>
</span>
, or
<code>
<span class="ts-text is-code">
linux_arm64_foobar
</code>
</span>
.
</p>
</p>
<p>
<p class="ts-text">
To manually install a plugin for testing, place the binary file into the
<code>
<span class="ts-text is-code">
/plugins/{plugin_name}/
</code>
</span>
folder within your Zoraxy installation directory.
</p>
</p>
<div class="ts-quote">
<p>
<p class="ts-text">
<strong>
<span class="ts-text is-heavy">
Warning:
</strong>
</span>
The binary name inside the folder must match the plugin folder name. For example, the binary should be named
<code>
<span class="ts-text is-code">
foobar
</code>
</span>
(or
<code>
<span class="ts-text is-code">
foobar.exe
</code>
</span>
on Windows) if placed in the
<code>
<span class="ts-text is-code">
/plugins/foobar/
</code>
</span>
folder. Avoid using names like
<code>
<span class="ts-text is-code">
foobar_plugin.exe
</code>
</span>
.
</p>
</p>
@ -223,58 +226,54 @@
For distribution, a plugin store system is used. The plugin store architecture is similar to the one built into the Arduino IDE, with a manager URL (a JSON file) listing all the plugins supported by that store. See the documentation section for more details on how to implement your own plugin store.
</p>
</p>
<div class="ts-divider"></div>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="plugin-vs-pull-request">
<div class="ts-header is-large">
Plugin vs Pull Request
</div>
Plugin vs Pull Request
</h2>
<p>
<p class="ts-text">
The Zoraxy plugin was introduced to address specific use cases that enhance its functionality. It serves as an extension to the core Zoraxy system, providing additional features and capabilities while maintaining the integrity of the core system.
</p>
</p>
<ul>
<li>
<div class="ts-list is-unordered">
<div class="item">
Designed to handle features that are challenging to integrate directly into the Zoraxy core.
<br>
</li>
<li>
</div>
<div class="item">
Caters to scenarios where certain features are only applicable in limited situations, avoiding unnecessary resource consumption for other users.
<br>
</li>
<li>
</div>
<div class="item">
Allows for frequent updates to specific code components without impacting the core&rsquo;s stability or causing downtime.
<br>
</li>
</ul>
<div class="ts-divider"></div>
<h3 id="when-should-you-add-a-core-pr-or-a-plugin">
<div class="ts-header">
When should you add a core PR or a plugin?
</div>
</div>
<div class="ts-divider has-top-spaced-large"></div>
<h3 id="when-should-you-add-a-core-pr-or-a-plugin">
When should you add a core PR or a plugin?
</h3>
<p>
<p class="ts-text">
In certain situations, implementing a feature as a plugin is more reasonable than directly integrating it into the Zoraxy core:
</p>
</p>
<ul>
<li>
<strong>
<div class="ts-list is-unordered">
<div class="item">
<span class="ts-text is-heavy">
Core PR
</strong>
</span>
: If the feature is relevant to most users and enhances Zoraxy&rsquo;s core functionality, consider submitting a core Pull Request (PR).
<br>
</li>
<li>
<strong>
</div>
<div class="item">
<span class="ts-text is-heavy">
Plugin
</strong>
</span>
: If the feature is targeted at a smaller user base or requires additional dependencies that not all users need, it should be developed as a plugin.
<br>
</li>
</ul>
</div>
</div>
<p>
The decision depends on the feature&rsquo;s general relevance and its impact on core stability. Plugins offer flexibility without burdening the core.
</p>

View File

@ -110,6 +110,9 @@
<a class="item" href="/html/2. Architecture/3. Configure.html">
Configure
</a>
<a class="item" href="/html/2. Architecture/4. Capture Modes.html">
Capture Modes
</a>
</div>
<a class="item">
Getting Started
@ -132,22 +135,20 @@
To start developing plugins, you will need the following installed on your computer
</p>
</p>
<ol>
<li>
<div class="ts-list is-ordered">
<div class="item">
The source code of Zoraxy
</li>
<li>
Go compiler
</li>
<li>
VSCode (recommended, or any editor of your choice)
</li>
</ol>
<div class="ts-divider"></div>
<h2 id="step-1-start-zoraxy-at-least-once">
<div class="ts-header is-large">
Step 1: Start Zoraxy at least once
</div>
<div class="item">
Go compiler
</div>
<div class="item">
VSCode (recommended, or any editor of your choice)
</div>
</div>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="step-1-start-zoraxy-at-least-once">
Step 1: Start Zoraxy at least once
</h2>
<p>
<p class="ts-text">
@ -167,20 +168,18 @@ sudo ./zoraxy
<p>
After the startup process completes, you would see a folder named &ldquo;plugins&rdquo; in the working directory of Zoraxy.
</p>
<div class="ts-divider"></div>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="steps-2-prepare-the-development-environment-for-zoraxy-plugin">
<div class="ts-header is-large">
Steps 2: Prepare the development environment for Zoraxy Plugin
</div>
Steps 2: Prepare the development environment for Zoraxy Plugin
</h2>
<p>
Next, you will need to think of a name for your plugin. Lets name our new plugin &ldquo;Lapwing&rdquo;.
</p>
<p>
<p class="ts-text">
<strong>
<span class="ts-text is-heavy">
Notes: Plugin name described in Introspect (will discuss this in later sessions) can contains space, but the folder and compiled binary filename must not contains space and special characters for platform compatibilities reasons.
</strong>
</span>
</p>
</p>
<p>
@ -189,96 +188,88 @@ sudo ./zoraxy
</p>
</p>
<h3 id="2-1-create-plugin-folder">
<div class="ts-header">
2.1 Create Plugin Folder
</div>
2.1 Create Plugin Folder
</h3>
<p>
<p class="ts-text">
Create a folder with your plugin name in the
<code>
<span class="ts-text is-code">
plugins
</code>
</span>
folder. After creating the folder, you would have something like
<code>
<span class="ts-text is-code">
plugins/Lapwing/
</code>
</span>
.
</p>
</p>
<h3 id="2-2-locate-and-copy-zoraxy-plugin-library">
<div class="ts-header">
2.2 Locate and copy Zoraxy Plugin library
</div>
2.2 Locate and copy Zoraxy Plugin library
</h3>
<p>
<p class="ts-text">
Locate the Zoraxy plugin library from the Zoraxy source code. You can find the
<code>
<span class="ts-text is-code">
zoraxy_plugin
</code>
</span>
Go module under
<code>
<span class="ts-text is-code">
src/mod/plugins/zoraxy_plugin
</code>
</span>
.
</p>
</p>
<p>
Copy the
<code>
<span class="ts-text is-code">
zoraxy_plugin
</code>
</span>
folder from the Zoraxy source code mod folder into the your plugin&rsquo;s mod folder. Let assume you use the same mod folder name as Zoraxy as
<code>
<span class="ts-text is-code">
mod
</code>
</span>
, then your copied library path should be
<code>
<span class="ts-text is-code">
plugins/Lapwing/mod/zoraxy_plugin
</code>
</span>
.
</p>
<h3 id="2-3-prepare-go-project-structure">
<div class="ts-header">
2.3 Prepare Go Project structure
</div>
2.3 Prepare Go Project structure
</h3>
<p>
<p class="ts-text">
Create the
<code>
<span class="ts-text is-code">
main.go
</code>
</span>
file for your plugin. In the example above, it would be located at
<code>
<span class="ts-text is-code">
plugins/Lapwing/main.go
</code>
</span>
.
</p>
</p>
<p>
<p class="ts-text">
Use
<code>
<span class="ts-text is-code">
go mod init yourdomain.com/foo/plugin_name
</code>
</span>
to initiate your plugin. By default the
<code>
<span class="ts-text is-code">
go.mod
</code>
</span>
file will be automatically generated by the go compiler. Assuming you are developing Lapwing with its source located on Github, this command would be
<code>
<span class="ts-text is-code">
go mod init github.com/your_user_name/Lapwing
</code>
</span>
.
</p>
</p>
<div class="ts-divider"></div>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="steps-3-open-plugin-folder-in-ide">
<div class="ts-header is-large">
Steps 3: Open plugin folder in IDE
</div>
Steps 3: Open plugin folder in IDE
</h2>
<p>
<p class="ts-text">

View File

@ -110,6 +110,9 @@
<a class="item" href="/html/2. Architecture/3. Configure.html">
Configure
</a>
<a class="item" href="/html/2. Architecture/4. Capture Modes.html">
Capture Modes
</a>
</div>
<a class="item">
Getting Started
@ -132,17 +135,17 @@
The Zoraxy Plugin uses a 3 steps approach to get information from plugin, setup the plugin and forward request to plugin. The name of the steps are partially referred from dbus designs as followings.
</p>
</p>
<ol>
<li>
<div class="ts-list is-ordered">
<div class="item">
Introspect
</li>
<li>
</div>
<div class="item">
Configure
</li>
<li>
</div>
<div class="item">
Forwarding
</li>
</ol>
</div>
</div>
<p>
<p class="ts-text">
The overall flow looks like this.

View File

@ -110,6 +110,9 @@
<a class="item" href="/html/2. Architecture/3. Configure.html">
Configure
</a>
<a class="item" href="/html/2. Architecture/4. Capture Modes.html">
Capture Modes
</a>
</div>
<a class="item">
Getting Started
@ -134,22 +137,22 @@
</p>
<p>
<p class="ts-text">
<strong>
<span class="ts-text is-heavy">
This is a pre-defined structure where the plugin must provide to Zoraxy
</strong>
</span>
when the plugin is being started with the
<code>
<span class="ts-text is-code">
-introspect
</code>
</span>
flag.
</p>
</p>
<p>
<p class="ts-text">
The introspect structure is defined under the
<code>
<span class="ts-text is-code">
zoraxy_plugin
</code>
</span>
library, where both Zoraxy and plugin should use. As of writing, the structure of introspect is like this.
</p>
</p>
@ -200,17 +203,15 @@
SubscriptionsEvents map[string]string `json:&quot;subscriptions_events&quot;` //Subscriptions events of your plugin, see Zoraxy documentation for more details
}
</code></pre>
<h2 id="introspect-triggering">
<div class="ts-header is-large">
Introspect Triggering
</div>
<h2 id="introspect-manual-triggering">
Introspect Manual Triggering
</h2>
<p>
<p class="ts-text">
To manually test if the introspect return is correct, you can try using the
<code>
<span class="ts-text is-code">
-introspect
</code>
</span>
flag on any Zoraxy plugin. You should be able to see an output like so.
</p>
</p>

View File

@ -110,6 +110,9 @@
<a class="item is-active" href="/html/2. Architecture/3. Configure.html">
Configure
</a>
<a class="item" href="/html/2. Architecture/4. Capture Modes.html">
Capture Modes
</a>
</div>
<a class="item">
Getting Started
@ -130,22 +133,22 @@
<p>
<p class="ts-text">
Configure or Configure Spec is the
<code>
<span class="ts-text is-code">
exec
</code>
</span>
call where Zoraxy start the plugin. The configure spec JSON structure is defined in
<code>
<span class="ts-text is-code">
zoraxy_plugin
</code>
</span>
library.
</p>
</p>
<p>
<p class="ts-text">
As the time of writing, the
<code>
<span class="ts-text is-code">
ConfigureSpec
</code>
</span>
only contains information on some basic info.
</p>
</p>
@ -159,22 +162,22 @@
<p>
<p class="ts-text">
The
<code>
<span class="ts-text is-code">
ConfigureSpec
</code>
</span>
struct will be parsed to JSON and pass to your plugin via the
<code>
<span class="ts-text is-code">
-configure=(json payload here)
</code>
</span>
.
</p>
</p>
<p>
<p class="ts-text">
In your plugin, you can use the
<code>
<span class="ts-text is-code">
zoraxy_plugin
</code>
</span>
library to parse it or parse it manually (if you are developing a plugin with other languages).
</p>
</p>

View File

@ -0,0 +1,331 @@
<!DOCTYPE html>
<html lang="en" class="is-white">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/png" href="/favicon.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
Capture Modes | Zoraxy Documentation
</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js" integrity="sha512-LhccdVNGe2QMEfI3x4DVV3ckMRe36TfydKss6mJpdHjNFiV07dFpS2xzeZedptKZrwxfICJpez09iNioiSZ3hA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocas-ui/5.0.2/tocas.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocas-ui/5.0.2/tocas.min.js"></script>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;500;700&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Code highlight -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<!-- additional languages -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/go.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/c.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/xml.min.js"></script>
<script>
hljs.highlightAll();
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/tomorrow-night-bright.css">
<style>
#msgbox{
position: fixed;
bottom: 1em;
right: 1em;
z-index: 9999;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
dialog[open] {
animation: fadeIn 0.3s ease-in-out;
}
code{
border-radius: 0.5rem;
}
</style>
<script src="/html/assets/theme.js"></script>
</head>
<body>
<div class="ts-content">
<div class="ts-container">
<div style="float: right;">
<button class="ts-button is-icon" id="darkModeToggle">
<span class="ts-icon is-moon-icon"></span>
</button>
</div>
<div class="ts-tab is-pilled">
<a href="" class="item" style="user-select: none;">
<img id="sysicon" class="ts-image" style="height: 30px" white_src="/html/assets/logo.png" dark_src="/html/assets/logo_white.png" src="/html/assets/logo.png"></img>
</a>
<a href="#!" class="is-active item">
Documents
</a>
<a href="#!" class="item">
Examples
</a>
</div>
</div>
</div>
<div class="ts-divider"></div>
<div>
<div class="has-padded">
<div class="ts-grid mobile:is-stacked">
<div class="column is-4-wide">
<div class="ts-box">
<div class="ts-menu is-end-icon">
<a class="item">
Introduction
<span class="ts-icon is-caret-down-icon"></span>
</a>
<div class="ts-menu is-dense is-small is-horizontally-padded">
<a class="item" href="/html/1. Introduction/1. What is Zoraxy Plugin.html">
What is Zoraxy Plugin
</a>
<a class="item" href="/html/1. Introduction/2. Getting Started.html">
Getting Started
</a>
</div>
<a class="item">
Architecture
<span class="ts-icon is-caret-down-icon"></span>
</a>
<div class="ts-menu is-dense is-small is-horizontally-padded">
<a class="item" href="/html/2. Architecture/1. Plugin Architecture.html">
Plugin Architecture
</a>
<a class="item" href="/html/2. Architecture/2. Introspect.html">
Introspect
</a>
<a class="item" href="/html/2. Architecture/3. Configure.html">
Configure
</a>
<a class="item is-active" href="/html/2. Architecture/4. Capture Modes.html">
Capture Modes
</a>
</div>
<a class="item">
Getting Started
<span class="ts-icon is-caret-down-icon"></span>
</a>
<a class="item" href="/html/index.html">
index
</a>
</div>
</div>
</div>
<div class="column is-12-wide">
<div class="ts-box">
<div class="ts-container is-padded has-top-padded-large">
<h1 id="capture-modes">
Capture Modes
</h1>
<p>
<p class="ts-text">
As you can see in the Introspect section, there are two types of capture mode in Zoraxy plugin API.
</p>
</p>
<div class="ts-list is-unordered">
<div class="item">
Static Capture Mode
</div>
<div class="item">
Dynamic Capture Mode
</div>
</div>
<p>
<span class="ts-text is-heavy">
Notes: When this document mention the term &ldquo;endpoint&rdquo;, it means a particular sub-path on the plugin side. For example
<span class="ts-text is-code">
/capture
</span>
or
<span class="ts-text is-code">
/sniff
</span>
. In actual implementation, this can be a
<span class="ts-text is-code">
http.HandleFunc
</span>
or
<span class="ts-text is-code">
http.Handle
</span>
depends on the plugin implementation.
</span>
</p>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="static-capture-mode">
Static Capture Mode
</h2>
<p>
<p class="ts-text">
Static Capture Mode register a static path to Zoraxy, when the plugin is enabled on a certain HTTP proxy rule, all request that matches the static capture registered paths are forwarded to the plugin without asking first. The overall process is shown in the diagram below.
</p>
</p>
<p>
<div class="ts-image is-rounded" style="max-width: 800px">
<img src="img/4. Capture Modes/static_capture.png" alt="static_capture" />
</div>
</p>
<p>
<p class="ts-text">
The main benefit of static capture mode is that the capture paths are stored in radix tree. That means it takes O(logn) time to resolve the path and forward the request. Hence,
<span class="ts-text is-heavy">
this mode is generally faster
</span>
if your plugin always listens to a few certain paths for extended functionalities.
</p>
</p>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="dynamic-capture-mode">
Dynamic Capture Mode
</h2>
<p>
<p class="ts-text">
Dynamic Capture Mode register two endpoints to Zoraxy.
</p>
</p>
<div class="ts-list is-ordered">
<div class="item">
DynamicCaptureSniff - The sniffing endpoint where Zoraxy will first ask if the plugin want to handle this request
</div>
<div class="item">
DynamicCaptureIngress - The handling endpoint, where if the plugin reply the sniffing with &ldquo;YES&rdquo;, Zoraxy forward the incoming request to this plugin at this defined endpoint.
</div>
</div>
<p>
<p class="ts-text">
The whole process will takes a few request exchange between plugin and Zoraxy core. Since both of them are communicating via the loopback interface, speed should not be too big of a concern here.
</p>
</p>
<p>
<p class="ts-text">
The request handling flow is shown in the diagram below.
</p>
</p>
<p>
<div class="ts-image is-rounded" style="max-width: 800px">
<img src="img/4. Capture Modes/dynamic_capture.png" alt="dynamic_capture" />
</div>
</p>
<p>
<p class="ts-text">
Once Zoraxy receive a request from a client that matches one of the HTTP Proxy Rule, Zoraxy will forward the request header to all the plugins that matches the following criteria
</p>
</p>
<div class="ts-list is-ordered">
<div class="item">
The plugin is assigned to a tag that is currently attached to the given HTTP Proxy that the request is coming through
</div>
<div class="item">
The plugin is enabled and running
</div>
<div class="item">
The plugin has registered its dynamic capture sniffing endpoint in Introspect
</div>
</div>
<p>
<p class="ts-text">
Then the plugin
<span class="ts-text is-code">
/sniff
</span>
endpoint will receive some basic header information about the request, and response with
<span class="ts-text is-code">
SniffResultAccpet
</span>
or
<span class="ts-text is-code">
SniffResultSkip
</span>
to accept or reject handling such request. The response are defined in
<span class="ts-text is-code">
zoraxy_plugin
</span>
as a public type where you can access with
<span class="ts-text is-code">
zoraxy_plugin.SniffresultAccept
</span>
and
<span class="ts-text is-code">
zoraxy_plugin.SniffResultSkip
</span>
respectively.
</p>
</p>
<p>
<p class="ts-text">
Note that this shall only be used if static capture mode cannot satisfy your needs in implementation the feature you want, as
<span class="ts-text is-heavy">
dynamic capture is way slower than static capture mode
</span>
.
</p>
</p>
<div class="ts-divider has-top-spaced-large"></div>
<h2 id="mixing-capture-modes">
Mixing Capture Modes
</h2>
<p>
<p class="ts-text">
It is possible for you to mix both Static and Capture modes if that is what you want. A few thing you need to know about mixing both mode in single plugin
</p>
</p>
<div class="ts-list is-ordered">
<div class="item">
Static capture mode has higher priority than dynamic capture mode across all plugins. That means if you have a request that matches Plugin A&rsquo;s static capture path and Plugin B&rsquo;s dynamic capture, the request will be first handled by Plugin A
</div>
<div class="item">
The same plugin can register both static and dynamic capture modes. Similar to item (1), if the request has already captured by your static capture path, Zoraxy will not proceed and forward the request header to your dynamic sniffing endpoint.
</div>
<div class="item">
In case there is a collision in static capture paths between two plugins, the longest one will have priority. For example, if Plugin A registered
<span class="ts-text is-code">
/foo
</span>
and Plugin B registered
<span class="ts-text is-code">
/foo/bar
</span>
, when a request to
<span class="ts-text is-code">
/foo/bar/teacat
</span>
enter Zoraxy, Plugin B is used for handling such request.
</div>
</div>
</div>
<br>
<br>
</div>
</div>
</div>
</div>
</div>
<div class="ts-container">
<div class="ts-divider"></div>
<div class="ts-content">
<div class="ts-text">
Zoraxy © tobychui
<span class="thisyear">
2025
</span>
</div>
</div>
</div>
<script>
$(".thisyear").text(new Date().getFullYear());
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

View File

@ -110,6 +110,9 @@
<a class="item" href="/html/2. Architecture/3. Configure.html">
Configure
</a>
<a class="item" href="/html/2. Architecture/4. Capture Modes.html">
Capture Modes
</a>
</div>
<a class="item">
Getting Started
@ -133,14 +136,10 @@
Click on a topic in the side menu to begin navigating through the available resources and guides for developing and managing plugins.
</p>
<h2 id="faq">
<div class="ts-header is-large">
FAQ
</div>
FAQ
</h2>
<h3 id="what-skills-do-i-need-for-developing-a-plugin">
<div class="ts-header">
What skills do I need for developing a plugin?
</div>
What skills do I need for developing a plugin?
</h3>
<p>
<p class="ts-text">
@ -148,9 +147,7 @@
</p>
</p>
<h3 id="will-a-plugin-crash-the-whole-zoraxy">
<div class="ts-header">
Will a plugin crash the whole Zoraxy?
</div>
Will a plugin crash the whole Zoraxy?
</h3>
<p>
<p class="ts-text">
@ -158,9 +155,7 @@
</p>
</p>
<h3 id="can-i-sell-my-plugin">
<div class="ts-header">
Can I sell my plugin?
</div>
Can I sell my plugin?
</h3>
<p>
<p class="ts-text">
@ -168,9 +163,7 @@
</p>
</p>
<h3 id="how-can-i-add-my-plugin-to-the-official-plugin-store">
<div class="ts-header">
How can I add my plugin to the official plugin store?
</div>
How can I add my plugin to the official plugin store?
</h3>
<p>
<p class="ts-text">

View File

@ -39,6 +39,11 @@
"filename": "2. Architecture/3. Configure.md",
"title": "Configure",
"type": "file"
},
{
"filename": "2. Architecture/4. Capture Modes.md",
"title": "Capture Modes",
"type": "file"
}
]
},