Skip to content

Commit

Permalink
Add source code and tests, update support files (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
devanych committed Nov 12, 2021
1 parent 9fa438e commit 0ec9604
Show file tree
Hide file tree
Showing 28 changed files with 2,711 additions and 31 deletions.
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Expand Up @@ -40,7 +40,6 @@ jobs:
- windows-latest

php:
- 7.4
- 8.0

steps:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/mutation.yml
Expand Up @@ -34,7 +34,6 @@ jobs:
- ubuntu-latest

php:
- 7.4
- 8.0

steps:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/static.yml
Expand Up @@ -36,7 +36,6 @@ jobs:
- ubuntu-latest

php:
- 7.4
- 8.0

steps:
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
@@ -1,4 +1,4 @@
# _____ Change Log
# Yii Middleware Change Log

## 1.0.0 under development

Expand Down
24 changes: 12 additions & 12 deletions README.md
Expand Up @@ -2,31 +2,31 @@
<a href="https://github.com/yiisoft" target="_blank">
<img src="https://yiisoft.github.io/docs/images/yii_logo.svg" height="100px">
</a>
<h1 align="center">Yii _____</h1>
<h1 align="center">Yii Middleware</h1>
<br>
</p>

[![Latest Stable Version](https://poser.pugx.org/yiisoft/_____/v/stable.png)](https://packagist.org/packages/yiisoft/_____)
[![Total Downloads](https://poser.pugx.org/yiisoft/_____/downloads.png)](https://packagist.org/packages/yiisoft/_____)
[![Build status](https://github.com/yiisoft/_____/workflows/build/badge.svg)](https://github.com/yiisoft/_____/actions?query=workflow%3Abuild)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/yiisoft/_____/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/_____/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/_____/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/_____/?branch=master)
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2F_____%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/_____/master)
[![static analysis](https://github.com/yiisoft/_____/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/_____/actions?query=workflow%3A%22static+analysis%22)
[![type-coverage](https://shepherd.dev/github/yiisoft/_____/coverage.svg)](https://shepherd.dev/github/yiisoft/_____)
[![Latest Stable Version](https://poser.pugx.org/yiisoft/yii-middleware/v/stable.png)](https://packagist.org/packages/yiisoft/yii-middleware)
[![Total Downloads](https://poser.pugx.org/yiisoft/yii-middleware/downloads.png)](https://packagist.org/packages/yiisoft/yii-middleware)
[![Build status](https://github.com/yiisoft/yii-middleware/workflows/build/badge.svg)](https://github.com/yiisoft/yii-middleware/actions?query=workflow%3Abuild)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/yiisoft/yii-middleware/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/yii-middleware/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/yii-middleware/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/yii-middleware/?branch=master)
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2Fyii-middleware%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/yii-middleware/master)
[![static analysis](https://github.com/yiisoft/yii-middleware/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/yii-middleware/actions?query=workflow%3A%22static+analysis%22)
[![type-coverage](https://shepherd.dev/github/yiisoft/yii-middleware/coverage.svg)](https://shepherd.dev/github/yiisoft/yii-middleware)

The package ...

## Requirements

- PHP 7.4 or higher.
- PHP 8.0 or higher.

## Installation

The package could be installed with composer:

```shell
composer require yiisoft/_____ --prefer-dist
composer require yiisoft/yii-middleware --prefer-dist
```

## General usage
Expand Down Expand Up @@ -60,7 +60,7 @@ The code is statically analyzed with [Psalm](https://psalm.dev/). To run static

## License

The Yii _____ is free software. It is released under the terms of the BSD License.
The Yii Middleware is free software. It is released under the terms of the BSD License.
Please see [`LICENSE`](./LICENSE.md) for more information.

Maintained by [Yii Software](https://www.yiiframework.com/).
Expand Down
34 changes: 24 additions & 10 deletions composer.json
@@ -1,19 +1,21 @@
{
"name": "yiisoft/replace-with-package-name",
"name": "yiisoft/yii-middleware",
"type": "library",
"description": "_____",
"description": "Yii middleware",
"keywords": [
"_____"
"yii",
"framework",
"middleware"
],
"homepage": "https://www.yiiframework.com/",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/_____/issues?state=open",
"issues": "https://github.com/yiisoft/yii-middleware/issues?state=open",
"forum": "https://www.yiiframework.com/forum/",
"wiki": "https://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
"chat": "https://t.me/yii3en",
"source": "https://github.com/yiisoft/_____"
"source": "https://github.com/yiisoft/yii-middleware"
},
"funding": [
{
Expand All @@ -28,22 +30,34 @@
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": "^7.4|^8.0"
"php": "^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"yiisoft/aliases": "^2.0",
"yiisoft/friendly-exception": "^1.0",
"yiisoft/http": "^1.2",
"yiisoft/network-utilities": "^1.0",
"yiisoft/router": "^3.0@dev",
"yiisoft/validator": "^3.0@dev"
},
"require-dev": {
"httpsoft/http-message": "^1.0",
"phpunit/phpunit": "^9.5",
"roave/infection-static-analysis-plugin": "^1.9",
"roave/infection-static-analysis-plugin": "^1.10",
"spatie/phpunit-watcher": "^1.23",
"vimeo/psalm": "^4.9"
"vimeo/psalm": "^4.12",
"yiisoft/router-fastroute": "^3.0@dev"
},
"autoload": {
"psr-4": {
"Yiisoft\\_____\\": "src"
"Yiisoft\\Yii\\Middleware\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Yiisoft\\_____\\Tests\\": "tests"
"Yiisoft\\Yii\\Middleware\\Tests\\": "tests"
}
},
"config": {
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Expand Up @@ -18,7 +18,7 @@
</php>

<testsuites>
<testsuite name="Yii _____ tests">
<testsuite name="Yii middleware tests">
<directory>./tests</directory>
</testsuite>
</testsuites>
Expand Down
5 changes: 1 addition & 4 deletions psalm.xml
@@ -1,14 +1,11 @@
<?xml version="1.0"?>
<psalm
errorLevel="2"
errorLevel="4"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor"/>
</ignoreFiles>
</projectFiles>
</psalm>
Empty file removed src/.gitkeep
Empty file.
179 changes: 179 additions & 0 deletions src/BasicNetworkResolver.php
@@ -0,0 +1,179 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Yii\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;

use function count;
use function in_array;
use function is_array;
use function is_callable;
use function is_string;
use function strtolower;

/**
* Basic network resolver updates an instance of server request with protocol from special headers.
*
* It can be used in the following cases:
* - not required IP resolve to access the user's IP
* - user's IP is already resolved (eg `ngx_http_realip_module` or similar)
*/
final class BasicNetworkResolver implements MiddlewareInterface
{
private const DEFAULT_PROTOCOL_AND_ACCEPTABLE_VALUES = [
'http' => ['http'],
'https' => ['https', 'on'],
];

/**
* @psalm-var array<string, array|callable|null>
*/
private array $protocolHeaders = [];

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$newScheme = null;

foreach ($this->protocolHeaders as $header => $data) {
if (!$request->hasHeader($header)) {
continue;
}

$headerValues = $request->getHeader($header);

if (is_callable($data)) {
$newScheme = $data($headerValues, $header, $request);
if ($newScheme === null) {
continue;
}

if (!is_string($newScheme)) {
throw new RuntimeException('The scheme is neither string nor null.');
}

if ($newScheme === '') {
throw new RuntimeException('The scheme cannot be an empty string.');
}

break;
}

$headerValue = strtolower($headerValues[0]);

foreach ($data as $protocol => $acceptedValues) {
if (!in_array($headerValue, $acceptedValues, true)) {
continue;
}
$newScheme = $protocol;
break 2;
}
}

$uri = $request->getUri();

if ($newScheme !== null && $newScheme !== $uri->getScheme()) {
$request = $request->withUri($uri->withScheme($newScheme));
}

return $handler->handle($request);
}

/**
* With added header to check for determining whether the connection is made via HTTP or HTTPS (or any protocol).
*
* The match of header names and values is case-insensitive.
* It's not advisable to put insecure/untrusted headers here.
*
* Accepted types of values:
* - NULL (default): {{DEFAULT_PROTOCOL_AND_ACCEPTABLE_VALUES}}
* - callable: custom function for getting the protocol
* ```php
* ->withProtocolHeader('x-forwarded-proto', function(array $values, string $header, ServerRequestInterface $request) {
* return $values[0] === 'https' ? 'https' : 'http';
* return null; // If it doesn't make sense.
* });
* ```
* - array: The array keys are protocol string and the array value is a list of header values that indicate the protocol.
* ```php
* ->withProtocolHeader('x-forwarded-proto', [
* 'http' => ['http'],
* 'https' => ['https']
* ]);
* ```
*
* @param string $header
* @param array|callable|null $values
*
* @return self
*
* @see DEFAULT_PROTOCOL_AND_ACCEPTABLE_VALUES
*/
public function withAddedProtocolHeader(string $header, $values = null): self
{
$new = clone $this;
$header = strtolower($header);

if ($values === null) {
$new->protocolHeaders[$header] = self::DEFAULT_PROTOCOL_AND_ACCEPTABLE_VALUES;
return $new;
}

if (is_callable($values)) {
$new->protocolHeaders[$header] = $values;
return $new;
}

if (!is_array($values)) {
throw new RuntimeException('Accepted values is not array nor callable.');
}

if (count($values) === 0) {
throw new RuntimeException('Accepted values cannot be an empty array.');
}

$new->protocolHeaders[$header] = [];

foreach ($values as $protocol => $acceptedValues) {
if (!is_string($protocol)) {
throw new RuntimeException('The protocol must be type of string.');
}

if ($protocol === '') {
throw new RuntimeException('The protocol cannot be an empty string');
}

$new->protocolHeaders[$header][$protocol] = array_map('strtolower', (array) $acceptedValues);
}

return $new;
}

public function withoutProtocolHeader(string $header): self
{
$new = clone $this;
unset($new->protocolHeaders[strtolower($header)]);
return $new;
}

public function withoutProtocolHeaders(?array $headers = null): self
{
$new = clone $this;

if ($headers === null) {
$new->protocolHeaders = [];
return $new;
}

foreach ($headers as $header) {
$new = $new->withoutProtocolHeader($header);
}

return $new;
}
}
23 changes: 23 additions & 0 deletions src/Exception/BadUriPrefixException.php
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Yii\Middleware\Exception;

use Yiisoft\FriendlyException\FriendlyExceptionInterface;

final class BadUriPrefixException extends \Exception implements FriendlyExceptionInterface
{
public function getName(): string
{
return 'Bad URI prefix';
}

public function getSolution(): ?string
{
return "Most likely you have specified the wrong URI prefix.\n"
. "Make sure that path from the web address contains the specified prefix (immediately after the domain part).\n"
. "The prefix value usually begins with a slash and must not end with a slash.\n"
. "The prefix should be exact match. We're not trimming it or adding anything to it.";
}
}

0 comments on commit 0ec9604

Please sign in to comment.