Network-related utilities for host validation and URL processing in Gradio Lite applications. Provides functions for handling network requests and URL resolution in WebAssembly environments.
Utilities for validating and processing host information in Gradio Lite applications.
/**
* Special hostname used for Gradio Lite server identification
*/
const FAKE_LITE_HOST = "lite.local";
/**
* Check if URL points to a self-hosted resource
* @param url - URL to check
* @returns true if URL is self-hosted (localhost, 127.0.0.1, or lite.local)
*/
function is_self_host(url: URL): boolean;Usage Examples:
import { FAKE_LITE_HOST, is_self_host } from "@gradio/wasm/network";
// Check various URLs
const urls = [
new URL("http://localhost:8000/api"),
new URL("http://127.0.0.1:7860/predict"),
new URL("http://lite.local/stream"),
new URL("https://example.com/api"),
new URL("http://192.168.1.100:8080/data")
];
urls.forEach(url => {
const isSelfHosted = is_self_host(url);
console.log(`${url.href}: ${isSelfHosted ? 'Self-hosted' : 'External'}`);
});
// Output:
// http://localhost:8000/api: Self-hosted
// http://127.0.0.1:7860/predict: Self-hosted
// http://lite.local/stream: Self-hosted
// https://example.com/api: External
// http://192.168.1.100:8080/data: External
// Using FAKE_LITE_HOST constant
console.log(`Lite host identifier: ${FAKE_LITE_HOST}`);
// Create URLs for Gradio Lite
const liteApiUrl = new URL(`http://${FAKE_LITE_HOST}/api/predict`);
console.log(`Lite API URL: ${liteApiUrl.href}`);
console.log(`Is self-hosted: ${is_self_host(liteApiUrl)}`);Examples of using network utilities in WebAssembly worker contexts:
import { WorkerProxy } from "@gradio/wasm";
import { is_self_host, FAKE_LITE_HOST } from "@gradio/wasm/network";
class NetworkAwareWorkerProxy extends WorkerProxy {
constructor(options: WorkerProxyOptions) {
super(options);
}
async makeRequest(url: string, options: RequestInit = {}): Promise<Response> {
const requestUrl = new URL(url);
if (is_self_host(requestUrl)) {
console.log("Making request to self-hosted service:", url);
// Route through worker for self-hosted requests
return this.makeWorkerRequest(requestUrl, options);
} else {
console.log("Making direct request to external service:", url);
// Make direct fetch for external requests
return fetch(url, options);
}
}
private async makeWorkerRequest(url: URL, options: RequestInit): Promise<Response> {
// Convert fetch options to HttpRequest format
const httpRequest = {
method: (options.method as any) || "GET",
path: url.pathname + url.search,
query_string: url.search.slice(1), // Remove leading ?
headers: this.convertHeaders(options.headers),
body: options.body ? new TextEncoder().encode(options.body as string) : undefined
};
try {
const httpResponse = await this.httpRequest(httpRequest);
// Convert HttpResponse back to Response
return new Response(httpResponse.body, {
status: httpResponse.status,
headers: httpResponse.headers
});
} catch (error) {
throw new Error(`Worker request failed: ${error}`);
}
}
private convertHeaders(headers: HeadersInit | undefined): Record<string, string> {
if (!headers) return {};
if (headers instanceof Headers) {
const result: Record<string, string> = {};
headers.forEach((value, key) => {
result[key] = value;
});
return result;
}
if (Array.isArray(headers)) {
const result: Record<string, string> = {};
headers.forEach(([key, value]) => {
result[key] = value;
});
return result;
}
return headers as Record<string, string>;
}
}
// Usage
const networkWorker = new NetworkAwareWorkerProxy({
gradioWheelUrl: "https://example.com/gradio.whl",
gradioClientWheelUrl: "https://example.com/gradio_client.whl",
files: {},
requirements: [],
sharedWorkerMode: false
});
networkWorker.addEventListener("initialization-completed", async () => {
// Make requests with automatic routing
const liteResponse = await networkWorker.makeRequest(`http://${FAKE_LITE_HOST}/api/info`);
const externalResponse = await networkWorker.makeRequest("https://api.github.com/users/octocat");
console.log("Lite response:", await liteResponse.json());
console.log("External response:", await externalResponse.json());
});Integration patterns for Gradio Lite applications:
import { WorkerProxy } from "@gradio/wasm";
import { is_self_host, FAKE_LITE_HOST } from "@gradio/wasm/network";
class GradioLiteApp {
private worker: WorkerProxy;
private appUrl: URL;
constructor(options: WorkerProxyOptions) {
this.worker = new WorkerProxy(options);
this.appUrl = new URL(`http://${FAKE_LITE_HOST}/`);
this.setupWorker();
}
private setupWorker() {
this.worker.addEventListener("initialization-completed", () => {
console.log("Gradio Lite app ready");
this.startGradioApp();
});
}
private async startGradioApp() {
// Initialize Gradio application in the worker
await this.worker.runPythonCode(`
import gradio as gr
import json
def process_data(data):
"""Process input data and return results"""
return {"processed": True, "input": data, "timestamp": "2024-01-01"}
def stream_results():
"""Stream processing results"""
for i in range(10):
yield {"step": i, "progress": i / 10}
# Create Gradio interface
with gr.Blocks(title="Gradio Lite App") as demo:
gr.Markdown("# Gradio Lite WebAssembly App")
with gr.Row():
input_text = gr.Textbox(label="Input Data", placeholder="Enter data to process")
output_json = gr.JSON(label="Processed Output")
process_btn = gr.Button("Process Data")
process_btn.click(
fn=process_data,
inputs=input_text,
outputs=output_json
)
with gr.Row():
stream_btn = gr.Button("Start Streaming")
stream_output = gr.JSON(label="Stream Output")
stream_btn.click(
fn=stream_results,
outputs=stream_output
)
# Launch with lite.local host
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
debug=True
)
print(f"Gradio app started at http://${FAKE_LITE_HOST}:7860")
`);
}
async makeApiCall(endpoint: string, data?: any): Promise<any> {
const apiUrl = new URL(endpoint, this.appUrl);
if (!is_self_host(apiUrl)) {
throw new Error("API endpoint must be self-hosted for Gradio Lite");
}
const requestOptions: RequestInit = {
method: data ? "POST" : "GET",
headers: {
"Content-Type": "application/json"
}
};
if (data) {
requestOptions.body = JSON.stringify(data);
}
const httpRequest = {
method: requestOptions.method as any,
path: apiUrl.pathname + apiUrl.search,
query_string: apiUrl.search.slice(1),
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: requestOptions.body ? new TextEncoder().encode(requestOptions.body) : undefined
};
const response = await this.worker.httpRequest(httpRequest);
if (response.status >= 400) {
throw new Error(`API request failed: ${response.status}`);
}
const responseText = new TextDecoder().decode(response.body);
return JSON.parse(responseText);
}
async processData(inputData: string): Promise<any> {
return this.makeApiCall("/api/process_data", { data: inputData });
}
async getAppInfo(): Promise<any> {
return this.makeApiCall("/api/info");
}
createEventSource(endpoint: string) {
const eventUrl = new URL(endpoint, this.appUrl);
if (!is_self_host(eventUrl)) {
throw new Error("Event source endpoint must be self-hosted for Gradio Lite");
}
return new WasmWorkerEventSource(this.worker, eventUrl);
}
terminate() {
this.worker.terminate();
}
}
// Usage
const gradioApp = new GradioLiteApp({
gradioWheelUrl: "https://example.com/gradio.whl",
gradioClientWheelUrl: "https://example.com/gradio_client.whl",
files: {},
requirements: ["gradio", "numpy", "pandas"],
sharedWorkerMode: false
});
// Wait for app to be ready, then interact with it
setTimeout(async () => {
try {
// Make API calls
const appInfo = await gradioApp.getAppInfo();
console.log("App info:", appInfo);
const result = await gradioApp.processData("sample input data");
console.log("Processing result:", result);
// Set up event streaming
const eventSource = gradioApp.createEventSource("/api/stream");
eventSource.onmessage = (event) => {
console.log("Stream data:", JSON.parse(event.data));
};
} catch (error) {
console.error("App interaction failed:", error);
}
}, 5000);Utilities for network debugging and configuration:
import { is_self_host, FAKE_LITE_HOST } from "@gradio/wasm/network";
class NetworkDebugger {
static analyzeUrl(url: string | URL): {
url: URL;
isSelfHosted: boolean;
hostname: string;
port: string;
isLiteHost: boolean;
isLocalhost: boolean;
is127001: boolean;
} {
const urlObj = typeof url === 'string' ? new URL(url) : url;
return {
url: urlObj,
isSelfHosted: is_self_host(urlObj),
hostname: urlObj.hostname,
port: urlObj.port || (urlObj.protocol === 'https:' ? '443' : '80'),
isLiteHost: urlObj.hostname === FAKE_LITE_HOST,
isLocalhost: urlObj.hostname === 'localhost',
is127001: urlObj.hostname === '127.0.0.1'
};
}
static logNetworkConfiguration() {
const testUrls = [
"http://localhost:8000/api",
"http://127.0.0.1:7860/predict",
`http://${FAKE_LITE_HOST}/stream`,
"https://api.gradio.app/v1/spaces",
"http://192.168.1.100:8080/data"
];
console.log("Network Configuration Analysis:");
console.log("=====================================");
console.log(`FAKE_LITE_HOST: ${FAKE_LITE_HOST}`);
console.log("");
testUrls.forEach(url => {
const analysis = this.analyzeUrl(url);
console.log(`URL: ${url}`);
console.log(` Hostname: ${analysis.hostname}`);
console.log(` Port: ${analysis.port}`);
console.log(` Self-hosted: ${analysis.isSelfHosted}`);
console.log(` Lite host: ${analysis.isLiteHost}`);
console.log(` Localhost: ${analysis.isLocalhost}`);
console.log(` 127.0.0.1: ${analysis.is127001}`);
console.log("");
});
}
static validateGradioLiteUrl(url: string): { valid: boolean; reason?: string } {
try {
const urlObj = new URL(url);
if (!is_self_host(urlObj)) {
return { valid: false, reason: "URL must be self-hosted for Gradio Lite" };
}
if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
return { valid: false, reason: "URL must use HTTP or HTTPS protocol" };
}
return { valid: true };
} catch (error) {
return { valid: false, reason: `Invalid URL format: ${error.message}` };
}
}
}
// Usage
NetworkDebugger.logNetworkConfiguration();
// Validate URLs for Gradio Lite
const testUrls = [
`http://${FAKE_LITE_HOST}/api/predict`,
"http://localhost:7860/stream",
"https://external-api.com/data"
];
testUrls.forEach(url => {
const validation = NetworkDebugger.validateGradioLiteUrl(url);
console.log(`${url}: ${validation.valid ? '✓ Valid' : '✗ Invalid'}`);
if (!validation.valid) {
console.log(` Reason: ${validation.reason}`);
}
});Handling cross-origin requests and security in WebAssembly environments:
import { is_self_host, FAKE_LITE_HOST } from "@gradio/wasm/network";
class SecureNetworkManager {
private allowedOrigins: Set<string>;
private worker: WorkerProxy;
constructor(worker: WorkerProxy, allowedOrigins: string[] = []) {
this.worker = worker;
this.allowedOrigins = new Set([
'localhost',
'127.0.0.1',
FAKE_LITE_HOST,
...allowedOrigins
]);
}
isOriginAllowed(url: URL): boolean {
return this.allowedOrigins.has(url.hostname) || is_self_host(url);
}
async secureFetch(url: string, options: RequestInit = {}): Promise<Response> {
const requestUrl = new URL(url);
if (!this.isOriginAllowed(requestUrl)) {
throw new Error(`Origin not allowed: ${requestUrl.hostname}`);
}
// Add security headers for self-hosted requests
if (is_self_host(requestUrl)) {
const secureOptions = {
...options,
headers: {
...options.headers,
'X-Requested-With': 'GradioWasm',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff'
}
};
return this.makeWorkerRequest(requestUrl, secureOptions);
}
// For external requests, use fetch with CORS handling
return fetch(url, {
...options,
mode: 'cors',
credentials: 'omit' // Don't send credentials for external requests
});
}
private async makeWorkerRequest(url: URL, options: RequestInit): Promise<Response> {
const httpRequest = {
method: (options.method as any) || "GET",
path: url.pathname + url.search,
query_string: url.search.slice(1),
headers: this.convertHeaders(options.headers),
body: options.body ? new TextEncoder().encode(options.body as string) : undefined
};
const httpResponse = await this.worker.httpRequest(httpRequest);
return new Response(httpResponse.body, {
status: httpResponse.status,
headers: httpResponse.headers
});
}
private convertHeaders(headers: HeadersInit | undefined): Record<string, string> {
if (!headers) return {};
const result: Record<string, string> = {};
if (headers instanceof Headers) {
headers.forEach((value, key) => {
result[key] = value;
});
} else if (Array.isArray(headers)) {
headers.forEach(([key, value]) => {
result[key] = value;
});
} else {
Object.assign(result, headers);
}
return result;
}
addAllowedOrigin(hostname: string): void {
this.allowedOrigins.add(hostname);
}
removeAllowedOrigin(hostname: string): void {
this.allowedOrigins.delete(hostname);
}
getAllowedOrigins(): string[] {
return Array.from(this.allowedOrigins);
}
}
// Usage
const worker = new WorkerProxy(options);
const secureManager = new SecureNetworkManager(worker, ['trusted-api.com']);
worker.addEventListener("initialization-completed", async () => {
try {
// This will work - self-hosted
const liteResponse = await secureManager.secureFetch(`http://${FAKE_LITE_HOST}/api/info`);
console.log("Lite API response:", await liteResponse.json());
// This will work - allowed origin
const trustedResponse = await secureManager.secureFetch("https://trusted-api.com/data");
console.log("Trusted API response:", await trustedResponse.json());
// This will fail - not allowed
try {
await secureManager.secureFetch("https://malicious-site.com/api");
} catch (error) {
console.log("Blocked malicious request:", error.message);
}
} catch (error) {
console.error("Secure fetch failed:", error);
}
});