f14c0bad8c
CI / lint (pull_request) Successful in 1m35s
CI / typecheck (pull_request) Successful in 1m38s
CI / behave (3.11) (pull_request) Successful in 1m48s
CI / behave (3.12) (pull_request) Successful in 1m47s
CI / behave (3.13) (pull_request) Successful in 1m43s
CI / build (pull_request) Successful in 1m33s
CI / lint (push) Successful in 1m31s
CI / typecheck (push) Successful in 1m29s
CI / behave (3.11) (push) Successful in 1m37s
CI / behave (3.12) (push) Successful in 1m44s
CI / behave (3.13) (push) Successful in 1m36s
CI / build (push) Successful in 1m31s
ISSUES CLOSED: #2
299 lines
9.9 KiB
Markdown
299 lines
9.9 KiB
Markdown
# External Dependency Interceptor
|
|
|
|
The `ExternalDependenciesInterceptor` is a utility module designed to intercept and control HTTP/HTTPS requests made by RDFLib. This ensures that all web requests for resolving external ontologies are exclusively performed by CleverRDFLib, rather than being made autonomously by RDFLib.
|
|
|
|
## Purpose
|
|
|
|
RDFLib may autonomously make HTTP/HTTPS requests when loading ontologies, which can lead to:
|
|
|
|
- **Uncontrolled network access**: RDFLib making requests without CleverRDFLib's knowledge
|
|
- **Inconsistent behavior**: Different loading behavior depending on RDFLib's internal mechanisms
|
|
- **Security concerns**: Unauthorized or unexpected network requests
|
|
- **Testing difficulties**: Unpredictable network behavior during testing
|
|
|
|
The `ExternalDependenciesInterceptor` prevents RDFLib from making autonomous web requests by intercepting `urllib.request` calls and either blocking them or redirecting them to local files.
|
|
|
|
## Important Warnings
|
|
|
|
!!! warning "Client Application Compatibility"
|
|
This interceptor modifies the global `urllib.request` opener, which affects **all** code using `urllib.request` in the same Python process. If your client application or other libraries use `urllib.request` for HTTP/HTTPS requests, this interceptor will block or redirect those requests as well, potentially breaking your application.
|
|
|
|
!!! warning "Production Use"
|
|
This module is intended for:
|
|
- **Debugging**: Isolating RDFLib's network behavior during development and testing
|
|
- **Production environments**: Only when you can guarantee that no other code in your application uses `urllib.request` for HTTP/HTTPS requests
|
|
|
|
If your application or its dependencies use `urllib.request`, do **not** use this interceptor in production, as it will interfere with their functionality.
|
|
|
|
## Handler Types
|
|
|
|
The module provides several handler types for different use cases:
|
|
|
|
### Blocking Handlers
|
|
|
|
These handlers block all HTTP/HTTPS requests unless they are redirected to local files:
|
|
|
|
- **`ExtDepsRejecterAndRedirectorHTTPHandler`**: Blocks HTTP requests unless mapped to local files
|
|
- **`ExtDepsRejecterAndRedirectorHTTPSHandler`**: Blocks HTTPS requests unless mapped to local files
|
|
|
|
### Redirecting Handlers
|
|
|
|
These handlers redirect mapped URIs to local files but allow unmapped requests to proceed normally:
|
|
|
|
- **`LocalFileRedirectorHTTPHandler`**: Redirects HTTP requests to local files, falls back to default behavior
|
|
- **`LocalFileRedirectorHTTPSHandler`**: Redirects HTTPS requests to local files, falls back to default behavior
|
|
|
|
## Basic Usage
|
|
|
|
### Blocking All External Requests
|
|
|
|
To block all external HTTP/HTTPS requests (except those mapped to local files):
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import ExternalDependenciesInterceptor
|
|
|
|
# Create interceptor (uses blocking handlers by default)
|
|
interceptor = ExternalDependenciesInterceptor()
|
|
|
|
# Register the interceptor
|
|
interceptor.register_dependencies_interceptor()
|
|
|
|
# Now RDFLib cannot make autonomous HTTP/HTTPS requests
|
|
# All requests will be blocked unless mapped to local files
|
|
```
|
|
|
|
### Redirecting Requests to Local Files
|
|
|
|
To redirect specific URIs to local files while blocking others:
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import ExternalDependenciesInterceptor
|
|
|
|
# Create interceptor
|
|
interceptor = ExternalDependenciesInterceptor()
|
|
|
|
# Set up URI mappings (web URI -> local file path)
|
|
uri_mappings = {
|
|
"http://example.com/ontology.owl": "/path/to/local/ontology.owl",
|
|
"https://example.org/schema.ttl": "/path/to/local/schema.ttl"
|
|
}
|
|
|
|
# Configure mappings
|
|
interceptor.set_mappings(uri_mappings)
|
|
|
|
# Register the interceptor
|
|
interceptor.register_dependencies_interceptor()
|
|
|
|
# Now:
|
|
# - Mapped URIs will be served from local files
|
|
# - Unmapped URIs will be blocked (PermissionError)
|
|
```
|
|
|
|
### Using Redirecting Handlers (Non-Blocking)
|
|
|
|
To redirect mapped URIs to local files but allow unmapped requests to proceed:
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import (
|
|
ExternalDependenciesInterceptor,
|
|
LocalFileRedirectorHTTPHandler,
|
|
LocalFileRedirectorHTTPSHandler
|
|
)
|
|
|
|
# Create interceptor
|
|
interceptor = ExternalDependenciesInterceptor()
|
|
|
|
# Replace handlers with redirecting (non-blocking) handlers
|
|
interceptor.replace_handlers([
|
|
LocalFileRedirectorHTTPSHandler(),
|
|
LocalFileRedirectorHTTPHandler()
|
|
])
|
|
|
|
# Set up URI mappings
|
|
uri_mappings = {
|
|
"http://example.com/ontology.owl": "/path/to/local/ontology.owl"
|
|
}
|
|
interceptor.set_mappings(uri_mappings)
|
|
|
|
# Register the interceptor
|
|
interceptor.register_dependencies_interceptor()
|
|
|
|
# Now:
|
|
# - Mapped URIs will be served from local files
|
|
# - Unmapped URIs will use default HTTP/HTTPS behavior (not blocked)
|
|
```
|
|
|
|
## Use Cases
|
|
|
|
### Testing and Development
|
|
|
|
During testing, you may want to ensure that RDFLib doesn't make unexpected network requests:
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import ExternalDependenciesInterceptor
|
|
|
|
# Set up interceptor for testing
|
|
interceptor = ExternalDependenciesInterceptor()
|
|
|
|
# Map test URIs to local test files
|
|
test_mappings = {
|
|
"http://test.example.com/ontology.owl": "test_data/test_ontology.owl"
|
|
}
|
|
interceptor.set_mappings(test_mappings)
|
|
interceptor.register_dependencies_interceptor()
|
|
|
|
# Now run your tests - RDFLib cannot make unexpected requests
|
|
```
|
|
|
|
### Production with Controlled Network Access
|
|
|
|
In production environments where you want to ensure all ontology loading goes through CleverRDFLib:
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import ExternalDependenciesInterceptor
|
|
|
|
# Only use this if you're certain no other code uses urllib.request
|
|
interceptor = ExternalDependenciesInterceptor()
|
|
|
|
# Optionally map known URIs to local files
|
|
uri_mappings = {
|
|
"http://known-ontology.example.com/owl": "/cached/ontology.owl"
|
|
}
|
|
interceptor.set_mappings(uri_mappings)
|
|
|
|
interceptor.register_dependencies_interceptor()
|
|
|
|
# Now all RDFLib requests are controlled
|
|
```
|
|
|
|
## Handler Details
|
|
|
|
### Blocking Handlers
|
|
|
|
**`ExtDepsRejecterAndRedirectorHTTPHandler`** and **`ExtDepsRejecterAndRedirectorHTTPSHandler`**:
|
|
|
|
- **Behavior**: Block all requests unless mapped to local files
|
|
- **Raises**: `PermissionError` for unmapped requests
|
|
- **Use case**: Strict control over network access
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import (
|
|
ExtDepsRejecterAndRedirectorHTTPHandler,
|
|
ExtDepsRejecterAndRedirectorHTTPSHandler
|
|
)
|
|
|
|
handler = ExtDepsRejecterAndRedirectorHTTPHandler()
|
|
handler.update_mappings({
|
|
"http://example.com/owl": "/local/file.owl"
|
|
})
|
|
|
|
# Mapped URI: Returns file content
|
|
# Unmapped URI: Raises PermissionError
|
|
```
|
|
|
|
### Redirecting Handlers
|
|
|
|
**`LocalFileRedirectorHTTPHandler`** and **`LocalFileRedirectorHTTPSHandler`**:
|
|
|
|
- **Behavior**: Redirect mapped URIs to local files, allow unmapped requests to proceed
|
|
- **Falls back**: Uses default HTTP/HTTPS handler for unmapped requests
|
|
- **Use case**: Selective redirection without blocking
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import (
|
|
LocalFileRedirectorHTTPHandler,
|
|
LocalFileRedirectorHTTPSHandler
|
|
)
|
|
|
|
handler = LocalFileRedirectorHTTPHandler()
|
|
handler.update_mappings({
|
|
"http://example.com/owl": "/local/file.owl"
|
|
})
|
|
|
|
# Mapped URI: Returns file content
|
|
# Unmapped URI: Uses default HTTP handler (normal network request)
|
|
```
|
|
|
|
## Updating Mappings
|
|
|
|
You can update URI mappings at runtime:
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import ExternalDependenciesInterceptor
|
|
|
|
interceptor = ExternalDependenciesInterceptor()
|
|
|
|
# Initial mappings
|
|
interceptor.set_mappings({
|
|
"http://example.com/owl": "/local/file1.owl"
|
|
})
|
|
|
|
interceptor.register_dependencies_interceptor()
|
|
|
|
# Later, update mappings
|
|
interceptor.set_mappings({
|
|
"http://example.com/owl": "/local/file1.owl",
|
|
"http://example.org/schema": "/local/file2.ttl"
|
|
})
|
|
```
|
|
|
|
## Custom Handlers
|
|
|
|
You can create custom handlers by extending the base handlers:
|
|
|
|
```python
|
|
from cleverrdf_lib.core.external_dep_interceptor import (
|
|
ExternalDependenciesInterceptor,
|
|
LocalFileRedirectorHTTPHandler
|
|
)
|
|
from urllib.request import Request
|
|
|
|
class CustomHTTPHandler(LocalFileRedirectorHTTPHandler):
|
|
"""Custom handler with additional logic."""
|
|
|
|
def http_open(self, req: Request):
|
|
# Add custom logic before redirection
|
|
url = req.get_full_url()
|
|
if url.endswith(".blocked"):
|
|
raise PermissionError("Blocked URL")
|
|
|
|
# Use parent implementation
|
|
return super().http_open(req)
|
|
|
|
# Use custom handler
|
|
interceptor = ExternalDependenciesInterceptor()
|
|
interceptor.replace_handlers([CustomHTTPHandler()])
|
|
interceptor.register_dependencies_interceptor()
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Use only when necessary**: Only install the interceptor when you need to control RDFLib's network behavior
|
|
2. **Test thoroughly**: If using in production, thoroughly test that no other code uses `urllib.request`
|
|
3. **Document usage**: Clearly document in your codebase that the interceptor is installed
|
|
4. **Use redirecting handlers**: Prefer redirecting handlers over blocking handlers when possible to avoid breaking other functionality
|
|
5. **Map all known URIs**: Pre-map all URIs you know will be requested to avoid blocking legitimate requests
|
|
|
|
## Limitations
|
|
|
|
- **Global effect**: The interceptor affects all `urllib.request` usage in the Python process
|
|
- **No selective interception**: Cannot selectively intercept only RDFLib requests
|
|
- **Process-wide**: Once installed, affects all code in the process
|
|
- **Cannot be uninstalled**: Once installed, the opener remains in effect
|
|
|
|
## When Not to Use
|
|
|
|
Do **not** use this interceptor if:
|
|
|
|
- Your application uses `urllib.request` for other purposes
|
|
- Your dependencies use `urllib.request` for HTTP/HTTPS requests
|
|
- You need RDFLib to make autonomous network requests
|
|
- You're unsure whether other code uses `urllib.request`
|
|
|
|
## Next Steps
|
|
|
|
- Learn about [Basic Usage](basic_usage.md) for loading ontologies
|
|
- Explore [Resolvers](resolvers.md) for controlling reference resolution
|
|
- Understand [Error Handling](error_handling.md) for managing loading errors
|
|
|