diff --git a/backend.js b/backend.js
index 9ec14bf1..8710d894 100644
--- a/backend.js
+++ b/backend.js
@@ -1,136 +1,5 @@
-/* -----------------------------------------------
- * Authors: QuiteAFancyEmerald, BinBashBanana (OlyB), YÖCTDÖNALD'S and the lime
- * Additional help from Divide and SexyDuceDuce >:D test aaaa
- * ----------------------------------------------- */
-const fs = require('fs');
-const path = require('path');
-const http = require('http');
-const https = require('https');
-const express = require('express');
-const corrosion = require('corrosion');
-const config = require('./config.json');
-const insert = require('./randomization.json');
-const app = express();
-const port = process.env.PORT || config.port;
-const server = http.createServer(app);
+(async() => {
+ await
+ import ('./src/backend.mjs');
-btoa = (str) => {
- return new Buffer.from(str).toString('base64');
-}
-
-atob = (str) => {
- return new Buffer.from(str, 'base64').toString('utf-8');
-}
-
-const text404 = fs.readFileSync(path.normalize(__dirname + '/views/404.html'), 'utf8');
-const pages = {
- 'index': 'index.html',
- /* Main */
- 'in': 'docs.html',
- 'faq': 'faq.html',
- 'j': 'hidden.html',
- 's': 'pages/frame.html',
- 'z': 'pages/surf.html',
- 'c': 'pages/nav/credits.html',
- 'x': 'pages/nav/bookmarklets.html',
- 'i': 'pages/nav/icons.html',
- 't': 'pages/nav/terms.html',
- /* Games */
- 'g': 'pages/nav/gtools.html',
- 'h': 'pages/nav/games5.html',
- 'el': 'pages/nav/emulators.html',
- 'f': 'pages/nav/flash.html',
- 'm': 'pages/nav/emulibrary.html',
- /* Proxies */
- 'q': 'pages/proxnav/corrosion.html',
- 'rh': 'pages/proxnav/rammerhead.html',
- 'w': 'pages/proxnav/womginx.html',
- /* Proxy Presets */
- 'sx': 'pages/proxnav/preset/searx.html',
- 'y': 'pages/proxnav/preset/youtube.html',
- 'd': 'pages/proxnav/preset/discord.html',
- 'r': 'pages/proxnav/preset/reddit.html',
- /* Misc */
- 'fg': 'archive/gfiles/flash/index.html',
- 'eg': 'archive/gfiles/rarch/index.html',
- 'vos': 'archive/vibeOS/index.html'
-};
-
-const cookingInserts = insert.content;
-const vegetables = insert.keywords;
-const charRandom = insert.chars;
-const splashRandom = insert.splash;
-const cacheBustList = {
- "styles.css": "styles-1644738239.css",
- "h5-nav.js": "h5-nav-1644738239.js",
- "desc.js": "desc-1644738239.js",
- "header.js": "header-1644738239.js",
- "footer.js": "footer-1644738239.js",
- "common.js": "common-1644738239.js",
- "links.js": "links-1644738239.js"
-};
-
-function randomListItem(lis) {
- return lis[Math.floor(Math.random() * lis.length)];
-}
-
-function insertCharset(str) {
- return str.replace(/|||/g, function() { return randomListItem(charRandom); }); // this needs to be inside a function, so that not every string is the same
-}
-
-function hutaoInsert(str) {
- return str.replace(//g, function() { return randomListItem(splashRandom); }); // this needs to be inside a function, so that not every string is the same
-}
-
-function insertCooking(str) {
- return str.replace(//g, function() { return '' + randomListItem(cookingInserts) + ''; }); // this needs to be inside a function, so that not every string is the same
-}
-
-function cacheBusting(str) {
- for (var item of Object.entries(cacheBustList)) {
- str = str.replace(new RegExp(item[0], "g"), item[1]);
- }
- return str;
-}
-
-function insertAll(str) {
- return insertCharset(hutaoInsert(insertCooking(cacheBusting(str))));
-}
-
-function tryReadFile(file) {
- return fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : text404;
-}
-
-let blacklist;
-
-const fetch = (...args) =>
- import ('node-fetch').then(({ default: fetch }) => fetch(...args));
-
-fetch("https://blocklistproject.github.io/Lists/alt-version/everything-nl.txt").then(response => response.text()).then(data => {
- blacklist = data.split("\n") && config.blacklist;
-});
-
-const proxy = new corrosion({
- title: config.title,
- prefix: config.prefix || '/search/',
- codec: config.codec || 'xor',
- ws: config.ws,
- requestMiddleware: [
- corrosion.middleware.blacklist(blacklist, 'Service not allowed due to bot protection! Make sure you are not trying to verify on a proxy.'),
- ],
-});
-
-proxy.bundleScripts();
-
-/* Querystring Navigation */
-app.get('/', async(req, res) => res.send(insertAll(tryReadFile(path.normalize(__dirname + '/views/' + (['/', '/?'].includes(req.url) ? pages.index : pages[Object.keys(req.query)[0]]))))));
-
-/* Static Files Served */
-app.use(express.static(path.normalize(__dirname + '/views')));
-app.use((req, res) => {
- if (req.url.startsWith(proxy.prefix)) return proxy.request(req, res);
- res.status(404).send(insertAll(text404));
-});
-
-server.listen(port);
-console.log('Holy Unblocker is listening on port ' + port + '. This is simply a public for Holy Unblocker. Certain functions may not work properly.');
\ No newline at end of file
+})();
\ No newline at end of file
diff --git a/bare/.gitignore b/bare/.gitignore
new file mode 100644
index 00000000..8a775b3b
--- /dev/null
+++ b/bare/.gitignore
@@ -0,0 +1,3 @@
+/package-lock.json
+/node_modules/
+/tls/
\ No newline at end of file
diff --git a/bare/EncodeProtocol.mjs b/bare/EncodeProtocol.mjs
new file mode 100644
index 00000000..ccaccbea
--- /dev/null
+++ b/bare/EncodeProtocol.mjs
@@ -0,0 +1,57 @@
+const valid_chars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
+const reserved_chars = "%";
+
+export function valid_protocol(protocol){
+ protocol = protocol.toString();
+
+ for(let i = 0; i < protocol.length; i++){
+ const char = protocol[i];
+
+ if(!valid_chars.includes(char)){
+ return false;
+ }
+ }
+
+ return true;
+}
+
+export function encode_protocol(protocol){
+ protocol = protocol.toString();
+
+ let result = '';
+
+ for(let i = 0; i < protocol.length; i++){
+ const char = protocol[i];
+
+ if(valid_chars.includes(char) && !reserved_chars.includes(char)){
+ result += char;
+ }else{
+ const code = char.charCodeAt();
+ result += '%' + code.toString(16).padStart(2, 0);
+ }
+ }
+
+ return result;
+}
+
+export function decode_protocol(protocol){
+ if(typeof protocol != 'string')throw new TypeError('protocol must be a string');
+
+ let result = '';
+
+ for(let i = 0; i < protocol.length; i++){
+ const char = protocol[i];
+
+ if(char == '%'){
+ const code = parseInt(protocol.slice(i + 1, i + 3), 16);
+ const decoded = String.fromCharCode(code);
+
+ result += decoded;
+ i += 2;
+ }else{
+ result += char;
+ }
+ }
+
+ return result;
+}
\ No newline at end of file
diff --git a/bare/Example.mjs b/bare/Example.mjs
new file mode 100644
index 00000000..d29caad4
--- /dev/null
+++ b/bare/Example.mjs
@@ -0,0 +1,46 @@
+import { Server as HTTPServer } from 'node:http';
+import { Server as BareServer } from './Server.mjs';
+
+const bare_server = new BareServer({
+ prefix: '/bare/',
+});
+
+const my_server = new HTTPServer();
+
+my_server.on('request', (request, response) => {
+ // .route_request() will return true if the request's URL points to the bare server's prefix.
+ if(bare_server.route_request(request, response)){
+ return;
+ }
+
+ // send a response for web crawlers/discoverers
+
+ const message = Buffer.from(`This server handles TOMP bare requests on the prefix: ${bare_server.prefix}`);
+
+ response.writeHead(200, {
+ 'content-type': 'text/plain',
+ 'content-length': message.byteLength,
+ });
+
+ response.end(message);
+});
+
+my_server.on('upgrade', (request, socket, head) => {
+ if(bare_server.route_upgrade(request, socket, head)){
+ return;
+ }
+
+ // All upgrade sockets should go to TOMP
+ // Because we have nothing to do with the socket, we will close it.
+
+ socket.end();
+});
+
+my_server.on('listening', () => {
+ console.log('Listening on localhost:80');
+});
+
+my_server.listen({
+ host: 'localhost',
+ port: 80,
+});
\ No newline at end of file
diff --git a/bare/HeaderUtil.mjs b/bare/HeaderUtil.mjs
new file mode 100644
index 00000000..b9d1be66
--- /dev/null
+++ b/bare/HeaderUtil.mjs
@@ -0,0 +1,33 @@
+export function ObjectFromRawHeaders(raw){
+ const result = Object.setPrototypeOf({}, null);
+
+ for(let i = 0; i < raw.length; i += 2){
+ let [header,value] = raw.slice(i, i + 2);
+ if (result[header] != void[]) result[header] = [].concat(result[header], value);
+ else result[header] = value;
+ }
+
+ return result;
+}
+
+export function RawHeaderNames(raw){
+ const result = [];
+
+ for(let i = 0; i < raw.length; i += 2){
+ if(!result.includes(i))result.push(raw[i]);
+ }
+
+ return result;
+}
+
+export function MapHeaderNamesFromArray(/*Array*/ from, /*Object*/ to){
+ for(let header of from) {
+ if(to[header.toLowerCase()] != void[]){
+ const value = to[header.toLowerCase()];
+ delete to[header.toLowerCase()];
+ to[header] = value;
+ }
+ }
+
+ return to;
+};
\ No newline at end of file
diff --git a/bare/LICENSE b/bare/LICENSE
new file mode 100644
index 00000000..e62ec04c
--- /dev/null
+++ b/bare/LICENSE
@@ -0,0 +1,674 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/bare/README.md b/bare/README.md
new file mode 100644
index 00000000..2d244ab6
--- /dev/null
+++ b/bare/README.md
@@ -0,0 +1,48 @@
+# TOMP Bare Server
+
+This repository implements the TompHTTP bare server. See the specification [here](https://github.com/tomphttp/specifications/blob/master/BareServerV1.md).
+
+## Usage
+
+We provide a command-line interface for creating a server.
+
+For more features, specify the `--help` option when running the CLI.
+
+### Quickstart
+
+1. Clone the repository locally
+```sh
+git clone https:/github.com/tomphttp/bare-server-node.git
+```
+
+2. Enter the folder
+```sh
+cd bare-server-node
+```
+
+3. Install dependencies
+```sh
+npm install
+```
+
+3. Start the server
+```sh
+node ./Standalone.mjs --port 80 --host localhost
+```
+
+### TLS
+
+In the cloned repository (See [quickstart](#quickstart))
+
+1. Generate OpenSSL certificates (Unless you're bringing your own)
+```sh
+mkdir tls
+openssl genrsa -out tls/key.pem
+openssl req -new -key tls/key.pem -out tls/csr.pem
+openssl x509 -req -days 9999 -in tls/csr.pem -signkey tls/key.pem -out tls/cert.pem
+```
+
+2. Start the server
+```sh
+node ./Standalone.mjs --port 443 --host localhost --tls --cert tls/cert.pem --key tls/key.pem
+```
diff --git a/bare/Response.mjs b/bare/Response.mjs
new file mode 100644
index 00000000..cc86e6de
--- /dev/null
+++ b/bare/Response.mjs
@@ -0,0 +1,34 @@
+import { OutgoingMessage } from 'node:http';
+import { Stream } from 'node:stream';
+
+export class Response {
+ headers = Object.setPrototypeOf({}, null);
+ status = 200;
+ constructor(body, status, headers){
+ this.body = body;
+
+ if(typeof status === 'number'){
+ this.status = status;
+ }
+
+ if(typeof headers === 'object' && headers !== undefined && headers !== null){
+ Object.assign(this.headers, headers);
+ }
+ }
+ send(request){
+ if(!(request instanceof OutgoingMessage))throw new TypeError('Request must be an OutgoingMessage');
+
+ request.writeHead(this.status, this.headers);
+
+ if(this.body instanceof Stream){
+ this.body.pipe(request);
+ }else if(this.body instanceof Buffer){
+ request.write(this.body);
+ request.end();
+ }else{
+ request.end();
+ }
+
+ return true;
+ }
+};
\ No newline at end of file
diff --git a/bare/Server.mjs b/bare/Server.mjs
new file mode 100644
index 00000000..e4b3bbcd
--- /dev/null
+++ b/bare/Server.mjs
@@ -0,0 +1,146 @@
+import { v1, v1socket, v1wsmeta, v1wsnewmeta } from './V1.mjs';
+import { Response } from './Response.mjs';
+
+export class Server {
+ prefix = '/';
+ fof = this.json(404, { message: 'Not found.' });
+ maintainer = undefined;
+ project = {
+ name: 'TOMPHTTP NodeJS Bare Server',
+ repository: 'https://github.com/tomphttp/bare-server-node',
+ };
+ log_error = false;
+ constructor(directory, log_error, maintainer){
+ if(typeof log_error === 'boolean'){
+ this.log_error = true;
+ }
+
+ if(typeof maintainer === 'object' && maintainer === null){
+ this.maintainer = maintainer;
+ }
+
+ if(typeof directory !== 'string'){
+ throw new Error('Directory must be specified.')
+ }
+
+ if(!directory.startsWith('/') || !directory.endsWith('/')){
+ throw new RangeError('Directory must start and end with /');
+ }
+
+ this.directory = directory;
+ }
+ error(...args){
+ if(this.log_error){
+ console.error(...args);
+ }
+ }
+ json(status, json){
+ const send = Buffer.from(JSON.stringify(json, null, '\t'));
+
+ return new Response(send, status, {
+ 'content-type': 'application/json',
+ 'content-length': send.byteLength,
+ });
+ }
+ route_request(request, response){
+ if(request.url.startsWith(this.directory)){
+ this.request(request, response);
+ return true;
+ }else{
+ return false;
+ }
+ }
+ route_upgrade(request, socket, head){
+ if(request.url.startsWith(this.directory)){
+ this.upgrade(request, socket, head);
+ return true;
+ }else{
+ return false;
+ }
+ }
+ get instance_info(){
+ return {
+ versions: [ 'v1' ],
+ language: 'NodeJS',
+ memoryUsage: Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100,
+ maintainer: this.maintainer,
+ developer: this.project,
+ };
+ }
+ async upgrade(request, socket, head){
+ const service = request.url.slice(this.directory.length - 1);
+
+ try{
+ switch(service){
+ case'/v1/':
+ await v1socket(this, request, socket, head);
+ break;
+ default:
+ socket.end();
+ break;
+ }
+ }catch(err){
+ this.error(err);
+ socket.end();
+ }
+ }
+ async request(server_request, server_response){
+ const service = server_request.url.slice(this.directory.length - 1);
+ let response;
+
+ try{
+ switch(service){
+ case'/':
+
+ if(server_request.method != 'GET')response = this.json(405, { message: 'This route only accepts the GET method.' });
+ else response = this.json(200, this.instance_info);
+
+ break;
+ case'/v1/':
+
+ response = await v1(this, server_request);
+
+ break;
+ case'/v1/ws-meta':
+
+ response = await v1wsmeta(this, server_request);
+
+ break;
+ case'/v1/ws-new-meta':
+
+ response = await v1wsnewmeta(this, server_request);
+
+ break;
+ default:
+
+ response = this.fof;
+
+ }
+ }catch(err){
+ this.error(err);
+
+ if(err instanceof Error){
+ response = this.json(500, {
+ code: 'UNKNOWN',
+ id: `error.${err.name}`,
+ message: err.message,
+ stack: err.stack,
+ });
+ }else{
+ response = this.json(500, {
+ code: 'UNKNOWN',
+ id: 'error.Exception',
+ message: err,
+ stack: new Error(err).stack,
+ });
+ }
+ }
+
+ if(!(response instanceof Response)){
+ this.error('Response to', server_request.url, 'was not a response.');
+ response = this.fof;
+ }
+
+ response.send(server_response);
+ }
+};
\ No newline at end of file
diff --git a/bare/Standalone.mjs b/bare/Standalone.mjs
new file mode 100644
index 00000000..b87dc9ef
--- /dev/null
+++ b/bare/Standalone.mjs
@@ -0,0 +1,81 @@
+import { Server as BareServer } from './Server.mjs';
+import { Server as HTTPServer, Server } from 'node:http';
+import { Server as TLSHTTPServer } from 'node:https';
+import { readFile } from 'node:fs/promises';
+import { cwd } from 'node:process';
+
+import { program, Option } from 'commander';
+import { resolve } from 'node:path';
+
+const default_port = Symbol();
+
+program
+.addOption(new Option('--d, --directory ', 'Bare directory').default('/'))
+.addOption(new Option('--h, --host ', 'Listening host').default('localhost'))
+.addOption(new Option('--p, --port ', 'Listening port').default(default_port).env('PORT'))
+.addOption(new Option('--e, --errors', 'Error logging').default(false))
+.addOption(new Option('--tls', 'use HTTPS (TLS/SSL)'))
+.addOption(new Option('--cert ', 'TLS certificate').default(''))
+.addOption(new Option('--key ', 'TLS key').default(''))
+;
+
+program.parse(process.argv);
+
+const options = program.opts();
+
+const bare = new BareServer(options.directory, options.errors);
+console.info('Created Bare Server on directory:', options.directory);
+console.info('Error logging is', options.errors ? 'enabled.' : 'disabled.');
+
+let http;
+
+if(options.tls){
+ const tls = {};
+
+ if(options.key !== ''){
+ options.key = resolve(cwd(), options.key);
+ console.info('Reading key from file:', options.key);
+ tls.key = await readFile(options.key);
+ }
+
+ if(options.cert !== ''){
+ options.cert = resolve(cwd(), options.cert);
+ console.info('Reading certificate from file:', options.cert);
+ tls.cert = await readFile(options.cert);
+ }
+
+ http = new TLSHTTPServer(tls);
+ console.info('Created TLS HTTP server.');
+}else{
+ http = new HTTPServer();
+ console.info('Created HTTP server.');
+}
+
+http.on('request', (req, res) => {
+ if(bare.route_request(req, res))return;
+
+ res.writeHead(400);
+ res.send('Not found');
+});
+
+http.on('upgrade', (req, socket, head) => {
+ if(bare.route_upgrade(req, socket, head))return;
+ socket.end();
+});
+
+if(options.port === default_port){
+ if(options.tls){
+ options.port = 443;
+ }else{
+ options.port = 80;
+ }
+}
+
+http.on('listening', () => {
+ console.log(`HTTP server listening. View live at ${options.tls ? 'https:' : 'http:'}//${options.host}:${options.port}${options.directory}`);
+});
+
+http.listen({
+ host: options.host,
+ port: options.port,
+});
\ No newline at end of file
diff --git a/bare/V1.mjs b/bare/V1.mjs
new file mode 100644
index 00000000..75c809ff
--- /dev/null
+++ b/bare/V1.mjs
@@ -0,0 +1,384 @@
+import http from 'node:http';
+import https from 'node:https';
+import { MapHeaderNamesFromArray, RawHeaderNames } from './HeaderUtil.mjs';
+import { decode_protocol } from './EncodeProtocol.mjs';
+import { Response } from './Response.mjs';
+import { randomBytes } from 'node:crypto';
+import { promisify } from 'node:util';
+
+const randomBytesAsync = promisify(randomBytes);
+
+// max of 4 concurrent sockets, rest is queued while busy? set max to 75
+// const http_agent = http.Agent();
+// const https_agent = https.Agent();
+
+async function Fetch(server_request, request_headers, url){
+ const options = {
+ host: url.host,
+ port: url.port,
+ path: url.path,
+ method: server_request.method,
+ headers: request_headers,
+ };
+
+ let outgoing;
+
+ if(url.protocol === 'https:'){
+ outgoing = https.request(options);
+ }else if(url.protocol === 'http:'){
+ outgoing = http.request(options);
+ }else{
+ throw new RangeError(`Unsupported protocol: '${url.protocol}'`);
+ }
+
+ server_request.pipe(outgoing);
+
+ return await new Promise((resolve, reject) => {
+ outgoing.on('response', resolve);
+ outgoing.on('error', reject);
+ });
+}
+
+function load_forwarded_headers(request, forward, target){
+ const raw = RawHeaderNames(request.rawHeaders);
+
+ for(let header of forward){
+ for(let cap of raw){
+ if(cap.toLowerCase() == header){
+ // header exists and real capitalization was found
+ target[cap] = request.headers[header];
+ }
+ }
+ }
+}
+
+function read_headers(server_request, request_headers){
+ const remote = Object.setPrototypeOf({}, null);
+ const headers = Object.setPrototypeOf({}, null);
+
+ for(let remote_prop of ['host','port','protocol','path']){
+ const header = `x-bare-${remote_prop}`;
+
+ if(header in request_headers){
+ let value = request_headers[header];
+
+ if(remote_prop === 'port'){
+ value = parseInt(value);
+ if(isNaN(value))return {
+ error: {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was not a valid integer.`,
+ },
+ };
+ }
+
+ remote[remote_prop] = value;
+ }else{
+ return {
+ error: {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header was not specified.`,
+ },
+ };
+ }
+ }
+
+ if('x-bare-headers' in request_headers){
+ let json;
+
+ try{
+ json = JSON.parse(request_headers['x-bare-headers']);
+
+ for(let header in json){
+ if(typeof json[header] !== 'string' && !Array.isArray(json[header])){
+ return {
+ error: {
+ code: 'INVALID_BARE_HEADER',
+ id: `bare.headers.${header}`,
+ message: `Header was not a String or Array.`,
+ },
+ };
+ }
+ }
+ }catch(err){
+ return {
+ error: {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.${header}`,
+ message: `Header contained invalid JSON. (${err.message})`,
+ },
+ };
+ }
+
+ Object.assign(headers, json);
+ }else{
+ return {
+ error: {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.x-bare-headers`,
+ message: `Header was not specified.`,
+ },
+ };
+ }
+
+ if('x-bare-forward-headers' in request_headers){
+ let json;
+
+ try{
+ json = JSON.parse(request_headers['x-bare-forward-headers']);
+ }catch(err){
+ return {
+ error: {
+ code: 'INVALID_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `Header contained invalid JSON. (${err.message})`,
+ },
+ };
+ }
+
+ load_forwarded_headers(server_request, json, headers);
+ }else{
+ return {
+ error: {
+ code: 'MISSING_BARE_HEADER',
+ id: `request.headers.x-bare-forward-headers`,
+ message: `Header was not specified.`,
+ },
+ };
+ }
+
+ return { remote, headers };
+}
+
+export async function v1(server, server_request){
+ const response_headers = Object.setPrototypeOf({}, null);
+
+ response_headers['x-robots-tag'] = 'noindex';
+ response_headers['access-control-allow-headers'] = '*';
+ response_headers['access-control-allow-origin'] = '*';
+ response_headers['access-control-expose-headers'] = '*';
+
+ const { error, remote, headers } = read_headers(server_request, server_request.headers);
+
+ if(error){
+ // sent by browser, not client
+ if(server_request.method === 'OPTIONS'){
+ return new Response(undefined, 200, response_headers);
+ }else{
+ return server.json(400, error);
+ }
+ }
+
+ let response;
+
+ try{
+ response = await Fetch(server_request, headers, remote);
+ }catch(err){
+ if(err instanceof Error){
+ switch(err.code){
+ case'ENOTFOUND':
+ return server.json(500, {
+ code: 'HOST_NOT_FOUND',
+ id: 'request',
+ message: 'The specified host could not be resolved.',
+ });
+ case'ECONNREFUSED':
+ return server.json(500, {
+ code: 'CONNECTION_REFUSED',
+ id: 'response',
+ message: 'The remote rejected the request.',
+ });
+ case'ECONNRESET':
+ return server.json(500, {
+ code: 'CONNECTION_RESET',
+ id: 'response',
+ message: 'The request was forcibly closed.',
+ });
+ case'ETIMEOUT':
+ return server.json(500, {
+ code: 'CONNECTION_TIMEOUT',
+ id: 'response',
+ message: 'The response timed out.',
+ });
+ }
+ }
+
+ throw err;
+ }
+
+ for(let header in response.headers){
+ if(header === 'content-encoding' || header === 'x-content-encoding'){
+ response_headers['content-encoding'] = response.headers[header];
+ }else if(header === 'content-length'){
+ response_headers['content-length'] = response.headers[header];
+ }
+ }
+
+ response_headers['x-bare-headers'] = JSON.stringify(MapHeaderNamesFromArray(RawHeaderNames(response.rawHeaders), {...response.headers}));
+ response_headers['x-bare-status'] = response.statusCode
+ response_headers['x-bare-status-text'] = response.statusMessage;
+
+ return new Response(response, 200, response_headers);
+}
+
+// prevent users from specifying id=__proto__ or id=constructor
+const temp_meta = Object.setPrototypeOf({}, null);
+
+setInterval(() => {
+ for(let id in temp_meta){
+ if(temp_meta[id].expires < Date.now()){
+ delete temp_meta[id];
+ }
+ }
+}, 1e3);
+
+export async function v1wsmeta(server, server_request){
+ if(!('x-bare-id' in server_request.headers)){
+ return server.json(400, {
+ code: 'MISSING_BARE_HEADER',
+ id: 'request.headers.x-bare-id',
+ message: 'Header was not specified',
+ });
+ }
+
+ const id = server_request.headers['x-bare-id'];
+
+ if(!(id in temp_meta)){
+ return server.json(400, {
+ code: 'INVALID_BARE_HEADER',
+ id: 'request.headers.x-bare-id',
+ message: 'Unregistered ID',
+ });
+ }
+
+ const { meta } = temp_meta[id];
+
+ delete temp_meta[id];
+
+ return server.json(200, meta);
+}
+
+export async function v1wsnewmeta(server, server_request){
+ const id = (await randomBytesAsync(32)).toString('hex');
+
+ temp_meta[id] = {
+ expires: Date.now() + 30e3,
+ };
+
+ return new Response(Buffer.from(id.toString('hex')))
+}
+
+export async function v1socket(server, server_request, server_socket, server_head){
+ if(!server_request.headers['sec-websocket-protocol']){
+ server_socket.end();
+ return;
+ }
+
+ const [ first_protocol, data ] = server_request.headers['sec-websocket-protocol'].split(/,\s*/g);
+
+ if(first_protocol !== 'bare'){
+ server_socket.end();
+ return;
+ }
+
+ const {
+ remote,
+ headers,
+ forward_headers,
+ id,
+ } = JSON.parse(decode_protocol(data));
+
+ load_forwarded_headers(server_request, forward_headers, headers);
+
+ const options = {
+ host: remote.host,
+ port: remote.port,
+ path: remote.path,
+ headers,
+ method: server_request.method,
+ };
+
+ let request_stream;
+
+ let response_promise = new Promise((resolve, reject) => {
+ try{
+ if(remote.protocol === 'wss:'){
+ request_stream = https.request(options, res => {
+ reject(`Remote didn't upgrade the request`);
+ });
+ }else if(remote.protocol === 'ws:'){
+ request_stream = http.request(options, res => {
+ reject(`Remote didn't upgrade the request`);
+ });
+ }else{
+ return reject(new RangeError(`Unsupported protocol: '${remote.protocol}'`));
+ }
+
+ request_stream.on('upgrade', (...args) => {
+ resolve(args)
+ });
+
+ request_stream.on('error', reject);
+ request_stream.write(server_head);
+ request_stream.end();
+ }catch(err){
+ reject(err);
+ }
+ });
+
+ const [ response, socket, head ] = await response_promise;
+
+ if('id' in temp_meta){
+ if(typeof id !== 'string'){
+ socket.end();
+ return;
+ }
+
+ const meta = {
+ headers: MapHeaderNamesFromArray(RawHeaderNames(response.rawHeaders), {...response.headers}),
+ };
+
+ temp_meta[id].meta = meta;
+ }
+
+
+ const response_headers = [
+ `HTTP/1.1 101 Switching Protocols`,
+ `Upgrade: websocket`,
+ `Connection: Upgrade`,
+ `Sec-WebSocket-Protocol: bare`,
+ `Sec-WebSocket-Accept: ${response.headers['sec-websocket-accept']}`,
+ ];
+
+ if('sec-websocket-extensions' in response.headers){
+ response_headers.push(`Sec-WebSocket-Extensions: ${response.headers['sec-websocket-extensions']}`);
+ }
+
+ server_socket.write(response_headers.concat('', '').join('\r\n'));
+ server_socket.write(head);
+
+ socket.on('close', () => {
+ // console.log('Remote closed');
+ server_socket.end();
+ });
+
+ server_socket.on('close', () => {
+ // console.log('Serving closed');
+ socket.end();
+ });
+
+ socket.on('error', err => {
+ server.error('Remote socket error:', err);
+ server_socket.end();
+ });
+
+ server_socket.on('error', err => {
+ server.error('Serving socket error:', err);
+ socket.end();
+ });
+
+ socket.pipe(server_socket);
+ server_socket.pipe(socket);
+}
diff --git a/bare/package.json b/bare/package.json
new file mode 100644
index 00000000..4c1b2a19
--- /dev/null
+++ b/bare/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "tomp-bare-server",
+ "description": "Back.",
+ "version": "0.0.1",
+ "homepage": "https://github.com/tomphttp",
+ "bugs": {
+ "url": "https://github.com/tomphttp/bare-server-node/issues",
+ "email": "tomp@sys32.dev"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tomphttp/bare-server-node.git"
+ },
+ "author": {
+ "name": "TOMP Development",
+ "email": "tomp@sys32.dev",
+ "url": "https://github.com/tomphttp"
+ },
+ "keywords": [
+ "proxy",
+ "tomp",
+ "tomphttp",
+ "sys32"
+ ],
+ "license": "GPL-3.0",
+ "type": "module",
+ "dependencies": {
+ "commander": "^9.0.0"
+ }
+}
diff --git a/ecosystem.config.js b/ecosystem.config.js
index ecdffd90..6dcb8491 100644
--- a/ecosystem.config.js
+++ b/ecosystem.config.js
@@ -1,17 +1,18 @@
module.exports = {
apps: [{
- name: 'HolyUnblockerPublic',
+ name: 'HolyUB',
script: './backend.js',
env: {
- PORT: 8080,
+ PORT: 8078,
NODE_ENV: "development",
},
env_production: {
- PORT: 8080,
+ PORT: 8078,
NODE_ENV: "production",
},
instances: "1",
- exec_mode: "cluster",
+ exec_interpreter: "babel-node",
+ exec_mode: "fork",
autorestart: true,
exp_backoff_restart_delay: 100,
cron_restart: "*/10 * * * *",
diff --git a/package.json b/package.json
index df93977a..3a331fa0 100644
--- a/package.json
+++ b/package.json
@@ -15,13 +15,11 @@
"author": "Titanium Network",
"license": "MIT",
"dependencies": {
+ "babel": "^6.23.0",
"corrosion": "^1.0.0",
"express": "^4.17.1",
"mime-types": "^2.1.27",
"node-fetch": "^3.2.0",
"ws": "^7.5.3"
- },
- "engines": {
- "node": ">=12.20.0"
}
}
diff --git a/src/backend.mjs b/src/backend.mjs
new file mode 100644
index 00000000..6fbbbe20
--- /dev/null
+++ b/src/backend.mjs
@@ -0,0 +1,29 @@
+import http from 'http';
+import path from 'path';
+import express from 'express';
+import { readFile } from 'fs/promises';
+import pkg from './routes.mjs';
+import { paintSource, tryReadFile } from './randomization.mjs';
+import { proxy } from './proxy.mjs';
+
+const config = JSON.parse(await readFile(new URL('./config.json',
+ import.meta.url)));
+const { pages, text404 } = pkg;
+const __dirname = path.resolve();
+const port = process.env.PORT || config.port;
+const app = express();
+const router = express.Router();
+const server = http.createServer(app);
+proxy.bundleScripts();
+
+router.get('/', async(req, res) => res.send(paintSource(tryReadFile(path.normalize(__dirname + '/views/' + (['/', '/?'].includes(req.url) ? pages.index : pages[Object.keys(req.query)[0]]))))));
+app.use(router);
+app.use(express.static(path.normalize(__dirname + '/views')));
+app.disable('x-powered-by');
+app.use((req, res) => {
+ if (req.url.startsWith(proxy.prefix)) return proxy.request(req, res);
+ res.status(404).send(paintSource(text404));
+});
+
+server.listen(port);
+console.log('Holy Unblocker is listening on port ' + port + '.');
\ No newline at end of file
diff --git a/src/charinsert.js b/src/charinsert.js
deleted file mode 100644
index 68017372..00000000
--- a/src/charinsert.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -----------------------------------------------
-/* Author : Divide
-/* MIT license: http://opensource.org/licenses/MIT
-/* ----------------------------------------------- */
-
-var fs = require('fs'),
- path = require('path'),
- mime = require('mime-types'),
- char_insert = (str, hash) => {
- var output = '';
-
- str.split(' ').forEach((word, word_index) => (word.split('').forEach((chr, chr_index) => output += (!chr_index || chr_index == word.length) ? '' + chr.charCodeAt() + '' : '' + hash + '' + chr.charCodeAt() + ''), output += word_index != str.split(' ').length - 1 ? ' ' : ''));
-
- return output
- },
- hash = s => { for (var i = 0, h = 9; i < s.length;) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); return h ^ h >>> 9 },
- express_ip = req => {
- var ip = null,
- methods = [req.headers['cf-connecting-ip'], req.headers['x-real-ip'], req.headers['x-forwarded-for']];
-
- methods.filter(method => method).forEach(method => {
- if (ip) return;
-
- ip = method;
-
- if (ip.includes(',')) {
- ip = ip.split(',')[ip.split(',').length - 1].replace(' ', '');
- if (ip.length > 15) ip = ip.split(',')[0].replace(' ', '');
- }
- });
-
- return ip || '127.0.0.1';
- };
-
-module.exports = {
- static: public_path => (req, res, next) => {
- var pub_file = path.join(public_path, req.url);
-
- if (fs.existsSync(pub_file)) {
- if (fs.statSync(pub_file).isDirectory()) pub_file = path.join(pub_file, 'index.html');
- if (!fs.existsSync(pub_file)) return next();
-
- var mime_type = mime.lookup(pub_file),
- data = fs.readFileSync(pub_file),
- ext = path.extname(pub_file);
-
- if (ext == '.html') data = data.toString('utf8').replace(/char_?insert{([\s\S]*?)}/gi, (match, str) => char_insert(str, hash(express_ip(req))));
-
- res.contentType(mime_type).send(data);
- } else next();
- },
- parse: string => char_insert(string, '-1231'),
-}
\ No newline at end of file
diff --git a/config.json b/src/config.json
similarity index 87%
rename from config.json
rename to src/config.json
index ecd29f8a..de8279bc 100644
--- a/config.json
+++ b/src/config.json
@@ -3,7 +3,7 @@
"port": "8080",
"ssl": false,
"ws": true,
- "prefix": "/search/",
+ "prefix": "/service/",
"codec": "xor",
"blacklist": [
"accounts.google.com",
diff --git a/randomization.json b/src/data.json
similarity index 87%
rename from randomization.json
rename to src/data.json
index 702ce88d..a4ad5909 100644
--- a/randomization.json
+++ b/src/data.json
@@ -3,7 +3,7 @@
"", "", ""
],
"keywords": [
- "Example1", "Example2"
+ "Example1", "Example2", "Example3", "Example4", "Example5", "Example6", "Example7", "Example8", "Example9"
],
"content": [
"Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1Example1",
diff --git a/src/examples/proxy.js b/src/examples/proxy.js
deleted file mode 100644
index d8337abf..00000000
--- a/src/examples/proxy.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const http = require('http');
-const express = require('express');
-const corrosion = require('corrosion');
-const app = express();
-const port = process.env.PORT || 8443;
-const server = http.createServer(app);
-const examples = ['accounts.google.com', 'example.com'];
-
-let blacklist;
-const fetch = (...t) =>
- import ("node-fetch").then(({ default: e }) => e(...t));
-fetch("https://blocklistproject.github.io/Lists/alt-version/everything-nl.txt").then(t => t.text()).then(t => { blacklist = t.split("\n") && examples });
-
-const proxy = new corrosion({
- title: 'Untitled Document',
- prefix: '/search/',
- codec: 'xor',
- ws: true,
- requestMiddleware: [
- corrosion.middleware.blacklist(blacklist, 'Service not allowed due to bot protection! Make sure you are not trying to verify on a proxy.'),
- ],
-});
-
-proxy.bundleScripts();
-server.listen(port);
\ No newline at end of file
diff --git a/src/examples/static.js b/src/examples/static.js
deleted file mode 100644
index cb006d5b..00000000
--- a/src/examples/static.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-const http = require('http');
-const express = require('express');
-const app = express();
-const port = process.env.PORT || 8080;
-const server = http.createServer(app);
-const text404 = fs.readFileSync(path.normalize(__dirname + '/views/404.html'), 'utf8');
-const pages = { index: "index.html", in: "docs.html", faq: "faq.html", j: "hidden.html", s: "pages/frame.html", z: "pages/surf.html", c: "pages/nav/credits.html", x: "pages/nav/bookmarklets.html", i: "pages/nav/icons.html", t: "pages/nav/terms.html", g: "pages/nav/gtools.html", h: "pages/nav/games5.html", el: "pages/nav/emulators.html", f: "pages/nav/flash.html", m: "pages/nav/emulibrary.html", q: "pages/proxnav/corrosion.html", w: "pages/proxnav/womginx.html", y: "pages/proxnav/preset/youtube.html", d: "pages/proxnav/preset/discord.html", r: "pages/proxnav/preset/reddit.html", fg: "archive/gfiles/flash/index.html", eg: "archive/gfiles/rarch/index.html", vos: "archive/vibeOS/index.html" };
-const cacheBustList = { "common.js": "common-1643838852.js", "links.js": "links-1642900360.js" };
-
-function cacheBusting(str) {
- for (var item of Object.entries(cacheBustList)) {
- str = str.replace(new RegExp(item[0], "g"), item[1]);
- }
- return str;
-}
-
-function insertAll(str) {
- return cacheBusting(str);
-}
-
-function tryReadFile(file) {
- return fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : text404;
-}
-
-app.get('/', async(req, res) => res.send(insertAll(tryReadFile(path.normalize(__dirname + '/views/' + (['/', '/?'].includes(req.url) ? pages.index : pages[Object.keys(req.query)[0]]))))));
-app.use(express.static(path.normalize(__dirname + "/views"))), app.use((e, s) => { s.status(404).send(insertAll(text404)) });
-server.listen(port);
\ No newline at end of file
diff --git a/src/proxy.mjs b/src/proxy.mjs
new file mode 100644
index 00000000..2a1184e9
--- /dev/null
+++ b/src/proxy.mjs
@@ -0,0 +1,24 @@
+import { readFile } from 'fs/promises';
+import corrosion from 'corrosion';
+
+const config = JSON.parse(await readFile(new URL('./config.json',
+ import.meta.url)));
+
+let blacklist;
+
+const fetch = (...args) =>
+ import ('node-fetch').then(({ default: fetch }) => fetch(...args));
+
+fetch("https://blocklistproject.github.io/Lists/alt-version/everything-nl.txt").then(response => response.text()).then(data => {
+ blacklist = data.split("\n") && config.blacklist;
+});
+
+export const proxy = new corrosion({
+ title: config.title,
+ prefix: config.prefix || '/service/',
+ codec: config.codec || 'xor',
+ ws: config.ws,
+ requestMiddleware: [
+ corrosion.middleware.blacklist(blacklist, 'Service not allowed due to bot protection! Make sure you are not trying to verify on a proxy.'),
+ ],
+});
\ No newline at end of file
diff --git a/src/randomization.mjs b/src/randomization.mjs
new file mode 100644
index 00000000..3629c45c
--- /dev/null
+++ b/src/randomization.mjs
@@ -0,0 +1,34 @@
+import pkg from './routes.mjs';
+import { existsSync, readFileSync } from 'fs';
+const { cookingInserts, vegetables, charRandom, splashRandom, cacheBustList, text404 } = pkg;
+
+function randomListItem(lis) {
+ return lis[Math.floor(Math.random() * lis.length)];
+}
+
+function insertCharset(str) {
+ return str.replace(/|||/g, function() { return randomListItem(charRandom); });
+}
+
+function hutaoInsert(str) {
+ return str.replace(//g, function() { return randomListItem(splashRandom); });
+}
+
+function insertCooking(str) {
+ return str.replace(//g, function() { return '' + randomListItem(cookingInserts) + ''; }); // this needs to be inside a function, so that not every string is the same
+}
+
+function cacheBusting(str) {
+ for (var item of Object.entries(cacheBustList)) {
+ str = str.replace(new RegExp(item[0], "g"), item[1]);
+ }
+ return str;
+}
+
+export function paintSource(str) {
+ return insertCharset(hutaoInsert(insertCooking(cacheBusting(str))));
+}
+
+export function tryReadFile(file) {
+ return existsSync(file) ? readFileSync(file, 'utf8') : text404;
+}
\ No newline at end of file
diff --git a/src/routes.mjs b/src/routes.mjs
new file mode 100644
index 00000000..dfffa06d
--- /dev/null
+++ b/src/routes.mjs
@@ -0,0 +1,59 @@
+import { readFileSync } from 'fs';
+import path from 'path';
+import { readFile } from 'fs/promises';
+
+
+const insert = JSON.parse(await readFile(new URL('./data.json',
+ import.meta.url)));
+
+const __dirname = path.resolve();
+
+const text404 = readFileSync(path.normalize(__dirname + '/views/404.html'), 'utf8');
+const pages = {
+ 'index': 'index.html',
+ /* Main */
+ 'in': 'docs.html',
+ 'faq': 'faq.html',
+ 'j': 'hidden.html',
+ 's': 'pages/frame.html',
+ 'z': 'pages/surf.html',
+ 'c': 'pages/nav/credits.html',
+ 'x': 'pages/nav/bookmarklets.html',
+ 'i': 'pages/nav/icons.html',
+ 't': 'pages/nav/terms.html',
+ /* Games */
+ 'g': 'pages/nav/gtools.html',
+ 'h': 'pages/nav/games5.html',
+ 'el': 'pages/nav/emulators.html',
+ 'f': 'pages/nav/flash.html',
+ 'm': 'pages/nav/emulibrary.html',
+ /* Proxies */
+ 'q': 'pages/proxnav/ultraviolet.html',
+ 'rh': 'pages/proxnav/rammerhead.html',
+ 'w': 'pages/proxnav/womginx.html',
+ /* Proxy Presets */
+ 'sx': 'pages/proxnav/preset/searx.html',
+ 'y': 'pages/proxnav/preset/youtube.html',
+ 'd': 'pages/proxnav/preset/discord.html',
+ 'r': 'pages/proxnav/preset/reddit.html',
+ /* Misc */
+ 'fg': 'archive/gfiles/flash/index.html',
+ 'eg': 'archive/gfiles/rarch/index.html',
+ 'vos': 'archive/vibeOS/index.html'
+};
+
+const cookingInserts = insert.content;
+const vegetables = insert.keywords;
+const charRandom = insert.chars;
+const splashRandom = insert.splash;
+const cacheBustList = {
+ "styles.css": "styles-1644738239.css",
+ "h5-nav.js": "h5-nav-1644738239.js",
+ "desc.js": "desc-1644738239.js",
+ "header.js": "header-1644738239.js",
+ "footer.js": "footer-1644738239.js",
+ "common.js": "common-16451543478.js",
+ "links.js": "links-1644738239.js"
+};
+
+export default { pages, text404, cookingInserts, vegetables, charRandom, splashRandom, cacheBustList };
\ No newline at end of file
diff --git a/views/ads.txt b/views/ads.txt
new file mode 100644
index 00000000..59bfdc25
--- /dev/null
+++ b/views/ads.txt
@@ -0,0 +1 @@
+google.com, pub-1493380855147376, DIRECT, f08c47fec0942fa0
\ No newline at end of file
diff --git a/views/assets/img/uv.png b/views/assets/img/uv.png
new file mode 100644
index 00000000..9ee3ec59
Binary files /dev/null and b/views/assets/img/uv.png differ
diff --git a/views/assets/img/uv2.png b/views/assets/img/uv2.png
new file mode 100644
index 00000000..89a939da
Binary files /dev/null and b/views/assets/img/uv2.png differ
diff --git a/views/assets/js/common-1644738239.js b/views/assets/js/common-16451543478.js
similarity index 68%
rename from views/assets/js/common-1644738239.js
rename to views/assets/js/common-16451543478.js
index 24745fac..13d9355b 100644
--- a/views/assets/js/common-1644738239.js
+++ b/views/assets/js/common-16451543478.js
@@ -30,7 +30,30 @@ function setAuthCookie(s, lax) {
document.cookie = s + "; expires=" + (Date.now() + 259200) + "; SameSite=" + (lax ? "Lax" : "None") + "; domain=." + getDomain() + "; path=/; Secure;";
}
-let sxlink = 'https://searx.degenerate.info';
+const sx = 'bing.com' + '/search?q=';
+
+function omnibox(url) {
+ if (url.substring(0, 4) == "http") {
+ return url;
+ } else if (url.includes("." || "")) {
+ return "https://" + url;
+ } else {
+ return "https://" + sx + url;
+ }
+}
+
+const xor = {
+ encode(str) {
+ if (!str) return str;
+ return encodeURIComponent(str.toString().split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join(''));
+ },
+ decode(str) {
+ if (!str) return str;
+ let [input, ...search] = str.split('?');
+
+ return decodeURIComponent(input).split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt(0) ^ 2) : char).join('') + (search.length ? '?' + search.join('?') : '');
+ },
+};
/* To use:
* goProx.proxy(url-string, stealth-boolean-optional)
@@ -41,13 +64,13 @@ let sxlink = 'https://searx.degenerate.info';
*/
window.goProx = {
- corrosion: function(url, stealth) {
+ ultraviolet: function(url, stealth) {
setAuthCookie("__cor_auth=1", true);
- goToUrl("https://" + getDomain() + "/search/gateway?url=" + url, stealth);
+ goToUrl("https://" + getDomain() + "/service/" + xor.encode(omnibox(url)), stealth);
},
womginx: function(url, stealth) {
setAuthCookie("wgauth=yes", false);
- goToUrl("https://a." + getDomain() + "/main/" + url, stealth);
+ goToUrl("https://a." + getDomain() + "/main/" + omnibox(url), stealth);
},
searx: function(stealth) {
setAuthCookie("oldsmobile=badcar", true);
@@ -66,23 +89,23 @@ window.goProx = {
},
mcnow: function(stealth) {
setAuthCookie("__cor_auth=1", false);
- goToUrl("https://cdn." + getDomain() + "/search/gateway?url=" + ('https://now.gg/play/mojang/2534/minecraft-trial'), stealth);
+ goToUrl("https://cdn." + getDomain() + "/sw/" + xor.encode('https://now.gg/play/mojang/2534/minecraft-trial'), stealth);
},
glife: function(stealth) {
setAuthCookie("__cor_auth=1", false);
- goToUrl("https://cdn." + getDomain() + "/search/gateway?url=" + ('https://now.gg/play/lunime/5767/gacha-life'), stealth);
+ goToUrl("https://cdn." + getDomain() + "/sw/" + xor.encode('https://now.gg/play/lunime/5767/gacha-life'), stealth);
},
roblox: function(stealth) {
setAuthCookie("__cor_auth=1", false);
- goToUrl("https://cdn." + getDomain() + "/search/gateway?url=" + ('https://now.gg/play/roblox-corporation/5349/roblox'), stealth);
+ goToUrl("https://cdn." + getDomain() + "/sw/" + xor.encode('https://now.gg/play/roblox-corporation/5349/roblox'), stealth);
},
amongus: function(stealth) {
setAuthCookie("__cor_auth=1", false);
- goToUrl("https://cdn." + getDomain() + "/search/gateway?url=" + ('https://now.gg/play/innersloth-llc/4047/among-us'), stealth);
+ goToUrl("https://cdn." + getDomain() + "/sw/" + xor.encode('https://now.gg/play/innersloth-llc/4047/among-us'), stealth);
},
pubg: function(stealth) {
setAuthCookie("__cor_auth=1", false);
- goToUrl("https://cdn." + getDomain() + "/search/gateway?url=" + ('https://now.gg/play/proxima-beta/2609/pubg-mobile-resistance'), stealth);
+ goToUrl("https://cdn." + getDomain() + "/sw/" + xor.encode('https://now.gg/play/proxima-beta/2609/pubg-mobile-resistance'), stealth);
},
train: function(stealth) {
setAuthCookie("wgauth=yes", false);
diff --git a/views/assets/js/csel.js b/views/assets/js/csel.js
index fb1a7dc4..49ed0281 100644
--- a/views/assets/js/csel.js
+++ b/views/assets/js/csel.js
@@ -5,11 +5,11 @@
/* ----------------------------------------------- */
(function() {
- var date = new Date();
+ let date = new Date();
date.setFullYear(date.getFullYear() + 100);
date = date.toUTCString();
- var csel = document.getElementById("csel");
+ let csel = document.getElementById("csel");
function setCookie(name, value) {
document.cookie = name + "=" + encodeURIComponent(value) + "; expires=" + date + "; ";
@@ -20,23 +20,23 @@
}
async function readCookie(name) {
- var cookie = document.cookie.split("; ");
- var cookies = {};
- for (var i = 0; i < cookie.length; i++) {
- var p = cookie[i].split("=");
+ let cookie = document.cookie.split("; ");
+ let cookies = {};
+ for (let i = 0; i < cookie.length; i++) {
+ let p = cookie[i].split("=");
cookies[p[0]] = p[1];
}
return decodeURIComponent(cookies[name]);
}
function pageTitle(value) {
- var tag = document.getElementsByTagName("title")[0] || document.createElement("title");
+ let tag = document.getElementsByTagName("title")[0] || document.createElement("title");
tag.innerHTML = value;
document.head.appendChild(tag);
}
function pageIcon(value) {
- var tag = document.querySelector("link[rel*='icon']") || document.createElement("link");
+ let tag = document.querySelector("link[rel*='icon']") || document.createElement("link");
tag.rel = "icon";
tag.href = value;
document.head.appendChild(tag);
diff --git a/views/index.html b/views/index.html
index 3c1bbae6..4b1e2c51 100644
--- a/views/index.html
+++ b/views/index.html
@@ -13,6 +13,7 @@
+
@@ -37,6 +38,7 @@
+ ADSENSEID
diff --git a/views/index.js b/views/index.js
new file mode 100644
index 00000000..29e0b431
--- /dev/null
+++ b/views/index.js
@@ -0,0 +1,6 @@
+importScripts('./uv.bundle.js');
+importScripts('./uv.handler.js');
+
+__uv.client.location.overrideWorkerLocation(() => new URL('https://www.google.com'));
+
+console.log(postMessage.__uv$string);
\ No newline at end of file
diff --git a/views/pages/frame.html b/views/pages/frame.html
index 68c67822..41cb910a 100644
--- a/views/pages/frame.html
+++ b/views/pages/frame.html
@@ -37,6 +37,8 @@
}
*/
+
+
+
+
+
+
+
+
+
+
+
Ultraviolet (Beta)
+
+
The highly innovative proxy of Titanium Network using technologies such as service workers and sophisticated rewriting techniques with
+ CAPTCHA support.
+ Focusing on speed UV works with YouTube, now.gg, Spotify, CoolMathGames and various .io sites!
+ CAPTCHA is supported!
+ Ultraviolet is highly recommended due to its speed and enhanced support for almost every site making it one of the most advanced web proxies.
+ Sites with given support for logging in are Twitter, Spotify, Reddit, and Discord. YouTube allows for nearly full functionality.
+ Discord support on Rammerhead is a KNOWN bug. It is being worked on.
+
+
+ Common Errors with Solutions:
+ - Having issues with CAPTCHAs? It will take trial and error but try to go slow when it comes to solving them. CAPTCHA is a given but will take a few attempts.
+ - Try using Classic mode or doing 'Reload Frame'. ("cdn.example.com" cannot be reached.)
+ - You may not be able to login normally into a number of sites. Phone verification on a select number of sites may occur also with no real soluion.
+
+
+
diff --git a/views/pages/proxnav/preset/searx.html b/views/pages/proxnav/preset/searx.html
index 87e4c5a8..3986c1be 100644
--- a/views/pages/proxnav/preset/searx.html
+++ b/views/pages/proxnav/preset/searx.html
@@ -35,8 +35,7 @@
Sometimes the proxies are under high load so things may be slow, sorry. In that case simply wait for the page to load.
diff --git a/views/pages/proxnav/preset/youtube.html b/views/pages/proxnav/preset/youtube.html
index cd9315a2..c90eee42 100644
--- a/views/pages/proxnav/preset/youtube.html
+++ b/views/pages/proxnav/preset/youtube.html
@@ -36,15 +36,15 @@
Sometimes the proxies are under high load so things may be slow, sorry. In that case simply wait for the page to load.