Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
/vendor/
/composer.lock
/.phpunit.cache/
/.phpunit.result.cache
/phpunit.xml
/.idea/
/.vscode/
/.DS_Store
/build/
# IDE
.idea/
.vscode/
.duyler/

# Package
vendor/
coverage/
.phpunit.cache/
composer.lock
/.php-cs-fixer.cache
/.phpstan.cache
/coverage.xml
.phpactor.json

# Duyler
/docs/
/examples/

# For LLM
.claude/
.opencode/
.cursor/
.sisyphus/

# MacOS
/.DS_Store
59 changes: 59 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.PHONY: tests
tests:
docker-compose run --rm php vendor/bin/phpunit

.PHONY: infection
infection:
docker-compose run --rm php vendor/bin/infection

.PHONY: psalm
psalm:
docker-compose run --rm php vendor/bin/psalm

.PHONY: cs-fix
cs-fix:
docker-compose run --rm php vendor/bin/php-cs-fixer fix

.PHONY: rector
rector:
docker-compose run --rm php vendor/bin/rector

.PHONY: shell
shell:
docker-compose run --rm php /bin/bash

.PHONY: coverage
coverage:
docker-compose run --rm php vendor/bin/phpunit --coverage-html=coverage --coverage-text

.PHONY: update
update:
docker-compose run --rm php composer update

.PHONY: init
init:
@if [ -z "$(NAME)" ]; then \
echo "Error: Please specify a package name."; \
echo "Example: make init NAME=my-awesome-package"; \
exit 1; \
fi

@echo "Starting to initialize the package: $(NAME)..."

$(eval PASCAL_NAME=$(shell echo $(NAME) | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++){$$i=toupper(substr($$i,1,1)) substr($$i,2)}}1' OFS=''))

@sed -i.bak 's/package-template/$(NAME)/g' composer.json && rm composer.json.bak
@sed -i.bak 's/PackageTemplate/$(PASCAL_NAME)/g' composer.json && rm composer.json.bak

@sed -i.bak 's/package-template/$(NAME)/g' README.md && rm README.md.bak
@sed -i.bak 's/PackageTemplate/$(PASCAL_NAME)/g' README.md && rm README.md.bak

@sed -i.bak 's/package-template/$(NAME)/g' sonar-project.properties && rm sonar-project.properties.bak
@sed -i.bak 's/PackageTemplate/$(PASCAL_NAME)/g' sonar-project.properties && rm sonar-project.properties.bak

@echo "Package name (kebab-case): $(NAME)"
@echo "Namespace (PascalCase): $(PASCAL_NAME)"

docker-compose build
docker-compose run --rm php composer install

17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,12 @@ use Duyler\HttpServer\WebSocket\WebSocketConfig;
use Duyler\HttpServer\WebSocket\Connection;
use Duyler\HttpServer\WebSocket\Message;

// Secure by default - origin validation is enabled
$wsConfig = new WebSocketConfig(
maxMessageSize: 1048576,
pingInterval: 30,
validateOrigin: false,
// validateOrigin defaults to true for security
// allowedOrigins defaults to ['*'] - customize for your domains
);

$ws = new WebSocketServer($wsConfig);
Expand Down Expand Up @@ -247,6 +249,19 @@ while (true) {
}
```

#### Public WebSocket Endpoints

For public WebSocket endpoints that accept connections from any origin:

```php
// WARNING: This configuration is insecure and exposes your WebSocket
// to CSRF attacks. Only use for truly public endpoints.
$wsConfig = new WebSocketConfig(
validateOrigin: false, // Explicit opt-out of origin validation
allowedOrigins: ['*'], // Explicit wildcard
);
```

See `examples/websocket-chat.php` for a complete chat application example.

### Static File Serving
Expand Down
6 changes: 6 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
services:
php:
image: duyler/php-zts:8.5
volumes:
- .:/app
working_dir: /app
92 changes: 92 additions & 0 deletions coverage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@


Code Coverage Report:
2026-02-28 08:27:07

Summary:
Classes: 21.74% (10/46)
Methods: 49.13% (198/403)
Lines: 52.20% (1681/3220)

Duyler\HttpServer\Config\ServerConfig
Methods: 50.00% ( 1/ 2) Lines: 40.00% ( 16/ 40)
Duyler\HttpServer\Connection\Connection
Methods: 74.07% (20/27) Lines: 84.44% ( 38/ 45)
Duyler\HttpServer\Connection\ConnectionPool
Methods: 76.92% (10/13) Lines: 93.85% ( 61/ 65)
Duyler\HttpServer\ErrorHandler
Methods: 25.00% ( 2/ 8) Lines: 19.73% ( 29/147)
Duyler\HttpServer\Exception\HttpServerException
Methods: 100.00% ( 3/ 3) Lines: 100.00% ( 3/ 3)
Duyler\HttpServer\Handler\FileDownloadHandler
Methods: 0.00% ( 0/ 4) Lines: 67.44% ( 58/ 86)
Duyler\HttpServer\Handler\StaticFileHandler
Methods: 46.15% ( 6/13) Lines: 92.76% (141/152)
Duyler\HttpServer\Metrics\ServerMetrics
Methods: 100.00% (14/14) Lines: 100.00% ( 53/ 53)
Duyler\HttpServer\Parser\HttpParser
Methods: 37.50% ( 3/ 8) Lines: 75.28% ( 67/ 89)
Duyler\HttpServer\Parser\RequestParser
Methods: 85.71% ( 6/ 7) Lines: 97.09% (100/103)
Duyler\HttpServer\Parser\ResponseWriter
Methods: 50.00% ( 3/ 6) Lines: 73.44% ( 47/ 64)
Duyler\HttpServer\RateLimit\RateLimiter
Methods: 100.00% ( 8/ 8) Lines: 100.00% ( 46/ 46)
Duyler\HttpServer\Server
Methods: 25.00% ( 9/36) Lines: 44.56% (262/588)
Duyler\HttpServer\Socket\SocketErrorSuppressor
Methods: 0.00% ( 0/ 1) Lines: 80.00% ( 4/ 5)
Duyler\HttpServer\Socket\SslSocket
Methods: 30.00% ( 3/10) Lines: 12.50% ( 8/ 64)
Duyler\HttpServer\Socket\StreamSocket
Methods: 40.00% ( 4/10) Lines: 61.43% ( 43/ 70)
Duyler\HttpServer\Socket\StreamSocketResource
Methods: 71.43% ( 5/ 7) Lines: 84.48% ( 49/ 58)
Duyler\HttpServer\Upload\TempFileManager
Methods: 75.00% ( 3/ 4) Lines: 90.91% ( 10/ 11)
Duyler\HttpServer\WebSocket\Enum\Opcode
Methods: 100.00% ( 2/ 2) Lines: 100.00% ( 2/ 2)
Duyler\HttpServer\WebSocket\Frame
Methods: 83.33% ( 5/ 6) Lines: 79.76% ( 67/ 84)
Duyler\HttpServer\WebSocket\Handshake
Methods: 83.33% ( 5/ 6) Lines: 96.00% ( 48/ 50)
Duyler\HttpServer\WebSocket\Message
Methods: 100.00% ( 7/ 7) Lines: 100.00% ( 12/ 12)
Duyler\HttpServer\WebSocket\WebSocketConfig
Methods: 100.00% ( 2/ 2) Lines: 100.00% ( 37/ 37)
Duyler\HttpServer\WebSocket\WebSocketServer
Methods: 50.00% (10/20) Lines: 39.51% ( 32/ 81)
Duyler\HttpServer\WorkerPool\Balancer\LeastConnectionsBalancer
Methods: 100.00% ( 5/ 5) Lines: 100.00% ( 16/ 16)
Duyler\HttpServer\WorkerPool\Balancer\RoundRobinBalancer
Methods: 100.00% ( 5/ 5) Lines: 100.00% ( 13/ 13)
Duyler\HttpServer\WorkerPool\Config\WorkerPoolConfig
Methods: 0.00% ( 0/ 3) Lines: 35.71% ( 10/ 28)
Duyler\HttpServer\WorkerPool\IPC\FdPasser
Methods: 25.00% ( 1/ 4) Lines: 20.88% ( 19/ 91)
Duyler\HttpServer\WorkerPool\IPC\Message
Methods: 87.50% ( 7/ 8) Lines: 97.44% ( 38/ 39)
Duyler\HttpServer\WorkerPool\IPC\UnixSocketChannel
Methods: 77.78% ( 7/ 9) Lines: 63.49% ( 40/ 63)
Duyler\HttpServer\WorkerPool\Master\AbstractMaster
Methods: 50.00% ( 4/ 8) Lines: 37.50% ( 12/ 32)
Duyler\HttpServer\WorkerPool\Master\CentralizedMaster
Methods: 18.18% ( 2/11) Lines: 14.97% ( 25/167)
Duyler\HttpServer\WorkerPool\Master\ConnectionQueue
Methods: 100.00% ( 8/ 8) Lines: 100.00% ( 15/ 15)
Duyler\HttpServer\WorkerPool\Master\ConnectionRouter
Methods: 50.00% ( 2/ 4) Lines: 29.73% ( 11/ 37)
Duyler\HttpServer\WorkerPool\Master\MasterFactory
Methods: 50.00% ( 2/ 4) Lines: 87.69% ( 57/ 65)
Duyler\HttpServer\WorkerPool\Master\SharedSocketMaster
Methods: 10.00% ( 1/10) Lines: 10.06% ( 16/159)
Duyler\HttpServer\WorkerPool\Master\SocketManager
Methods: 44.44% ( 4/ 9) Lines: 78.48% ( 62/ 79)
Duyler\HttpServer\WorkerPool\Master\WorkerManager
Methods: 50.00% ( 5/10) Lines: 20.45% ( 9/ 44)
Duyler\HttpServer\WorkerPool\Process\ProcessInfo
Methods: 100.00% ( 9/ 9) Lines: 100.00% ( 61/ 61)
Duyler\HttpServer\WorkerPool\Signal\SignalHandler
Methods: 11.11% ( 1/ 9) Lines: 25.00% ( 8/ 32)
Duyler\HttpServer\WorkerPool\Util\SystemInfo
Methods: 30.77% ( 4/13) Lines: 38.30% ( 36/ 94)
Loading
Loading