scaleway-instance/lib/HeaderSelector.php

273 lines
22 KiB
PHP
Raw Normal View History

2025-02-24 15:03:32 +01:00
<?php
/**
* HeaderSelector
* PHP version 7.4
*
* @category Class
* @package OpenAPI\Client
* @author OpenAPI Generator team
* @link https://openapi-generator.tech
*/
/**
* Instance API
*
* Scaleway Instances are virtual machines in the cloud. Different [Instance types](https://www.scaleway.com/en/docs/compute/instances/reference-content/choosing-instance-type/) offer different technical specifications in terms of vCPU, RAM, bandwidth and storage. Once you have created your Instance and installed your image of choice (e.g. an operating system), you can [connect to your Instance via SSH](https://www.scaleway.com/en/docs/compute/instances/how-to/connect-to-instance/) to use it as you wish. When you are done using the Instance, you can delete it from your account. (switchcolumn) <Message type=\"tip\"> To retrieve information about the different [images](#path-images) available to install on Scaleway Instances, check out our [Marketplace API](https://www.scaleway.com/en/developers/api/marketplace). </Message> (switchcolumn) ## Concepts Refer to our [dedicated concepts page](https://www.scaleway.com/en/docs/compute/instances/concepts/) to find definitions of all concepts and terminology related to Instances. (switchcolumn) (switchcolumn) ## Quickstart 1. Configure your environment variables <Message type=\"note\"> This is an optional step that seeks to simplify your usage of the Instances API. See [Availability Zones](#availability-zones) below for help choosing an Availability Zone. You can find your Project ID in the [Scaleway console](https://console.scaleway.com/project/settings). </Message> ```bash export SCW_SECRET_KEY=\"<API secret key>\" export SCW_DEFAULT_ZONE=\"<Scaleway Availability Zone>\" export SCW_PROJECT_ID=\"<Scaleway Project ID>\" ``` 2. **Create an Instance**: Run the following command to create an Instance. You can customize the details in the payload (name, description, type, tags etc) to your needs: use the information below to adjust the payload as necessary. ```bash curl -X POST \\ -H \"X-Auth-Token: $SCW_SECRET_KEY\" \\ -H \"Content-Type: application/json\" \\ \"https://api.scaleway.com/instance/v1/zones/$SCW_DEFAULT_ZONE/servers\" \\ -d '{ \"name\": \"my-new-instance\", \"project\": \"'\"$SCW_PROJECT_ID\"'\", \"commercial_type\": \"GP1-S\", \"image\": \"ubuntu_noble\", \"enable_ipv6\": true, \"volumes\": { \"0\":{ \"size\": 300000000000, \"volume_type\": \"l_ssd\" } } }' ``` | Parameter | Description | Valid values | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | A name of your choice for the Instance (string)
*
* The version of the OpenAPI document: v1
* Generated by: https://openapi-generator.tech
* Generator version: 7.11.0
*/
/**
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
namespace OpenAPI\Client;
/**
* HeaderSelector Class Doc Comment
*
* @category Class
* @package OpenAPI\Client
* @author OpenAPI Generator team
* @link https://openapi-generator.tech
*/
class HeaderSelector
{
/**
* @param string[] $accept
* @param string $contentType
* @param bool $isMultipart
* @return string[]
*/
public function selectHeaders(array $accept, string $contentType, bool $isMultipart): array
{
$headers = [];
$accept = $this->selectAcceptHeader($accept);
if ($accept !== null) {
$headers['Accept'] = $accept;
}
if (!$isMultipart) {
if($contentType === '') {
$contentType = 'application/json';
}
$headers['Content-Type'] = $contentType;
}
return $headers;
}
/**
* Return the header 'Accept' based on an array of Accept provided.
*
* @param string[] $accept Array of header
*
* @return null|string Accept (e.g. application/json)
*/
private function selectAcceptHeader(array $accept): ?string
{
# filter out empty entries
$accept = array_filter($accept);
if (count($accept) === 0) {
return null;
}
# If there's only one Accept header, just use it
if (count($accept) === 1) {
return reset($accept);
}
# If none of the available Accept headers is of type "json", then just use all them
$headersWithJson = $this->selectJsonMimeList($accept);
if (count($headersWithJson) === 0) {
return implode(',', $accept);
}
# If we got here, then we need add quality values (weight), as described in IETF RFC 9110, Items 12.4.2/12.5.1,
# to give the highest priority to json-like headers - recalculating the existing ones, if needed
return $this->getAcceptHeaderWithAdjustedWeight($accept, $headersWithJson);
}
/**
* Detects whether a string contains a valid JSON mime type
*
* @param string $searchString
* @return bool
*/
public function isJsonMime(string $searchString): bool
{
return preg_match('~^application/(json|[\w!#$&.+-^_]+\+json)\s*(;|$)~', $searchString) === 1;
}
/**
* Select all items from a list containing a JSON mime type
*
* @param array $mimeList
* @return array
*/
private function selectJsonMimeList(array $mimeList): array {
$jsonMimeList = [];
foreach ($mimeList as $mime) {
if($this->isJsonMime($mime)) {
$jsonMimeList[] = $mime;
}
}
return $jsonMimeList;
}
/**
* Create an Accept header string from the given "Accept" headers array, recalculating all weights
*
* @param string[] $accept Array of Accept Headers
* @param string[] $headersWithJson Array of Accept Headers of type "json"
*
* @return string "Accept" Header (e.g. "application/json, text/html; q=0.9")
*/
private function getAcceptHeaderWithAdjustedWeight(array $accept, array $headersWithJson): string
{
$processedHeaders = [
'withApplicationJson' => [],
'withJson' => [],
'withoutJson' => [],
];
foreach ($accept as $header) {
$headerData = $this->getHeaderAndWeight($header);
if (stripos($headerData['header'], 'application/json') === 0) {
$processedHeaders['withApplicationJson'][] = $headerData;
} elseif (in_array($header, $headersWithJson, true)) {
$processedHeaders['withJson'][] = $headerData;
} else {
$processedHeaders['withoutJson'][] = $headerData;
}
}
$acceptHeaders = [];
$currentWeight = 1000;
$hasMoreThan28Headers = count($accept) > 28;
foreach($processedHeaders as $headers) {
if (count($headers) > 0) {
$acceptHeaders[] = $this->adjustWeight($headers, $currentWeight, $hasMoreThan28Headers);
}
}
$acceptHeaders = array_merge(...$acceptHeaders);
return implode(',', $acceptHeaders);
}
/**
* Given an Accept header, returns an associative array splitting the header and its weight
*
* @param string $header "Accept" Header
*
* @return array with the header and its weight
*/
private function getHeaderAndWeight(string $header): array
{
# matches headers with weight, splitting the header and the weight in $outputArray
if (preg_match('/(.*);\s*q=(1(?:\.0+)?|0\.\d+)$/', $header, $outputArray) === 1) {
$headerData = [
'header' => $outputArray[1],
'weight' => (int)($outputArray[2] * 1000),
];
} else {
$headerData = [
'header' => trim($header),
'weight' => 1000,
];
}
return $headerData;
}
/**
* @param array[] $headers
* @param float $currentWeight
* @param bool $hasMoreThan28Headers
* @return string[] array of adjusted "Accept" headers
*/
private function adjustWeight(array $headers, float &$currentWeight, bool $hasMoreThan28Headers): array
{
usort($headers, function (array $a, array $b) {
return $b['weight'] - $a['weight'];
});
$acceptHeaders = [];
foreach ($headers as $index => $header) {
if($index > 0 && $headers[$index - 1]['weight'] > $header['weight'])
{
$currentWeight = $this->getNextWeight($currentWeight, $hasMoreThan28Headers);
}
$weight = $currentWeight;
$acceptHeaders[] = $this->buildAcceptHeader($header['header'], $weight);
}
$currentWeight = $this->getNextWeight($currentWeight, $hasMoreThan28Headers);
return $acceptHeaders;
}
/**
* @param string $header
* @param int $weight
* @return string
*/
private function buildAcceptHeader(string $header, int $weight): string
{
if($weight === 1000) {
return $header;
}
return trim($header, '; ') . ';q=' . rtrim(sprintf('%0.3f', $weight / 1000), '0');
}
/**
* Calculate the next weight, based on the current one.
*
* If there are less than 28 "Accept" headers, the weights will be decreased by 1 on its highest significant digit, using the
* following formula:
*
* next weight = current weight - 10 ^ (floor(log(current weight - 1)))
*
* ( current weight minus ( 10 raised to the power of ( floor of (log to the base 10 of ( current weight minus 1 ) ) ) ) )
*
* Starting from 1000, this generates the following series:
*
* 1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
*
* The resulting quality codes are closer to the average "normal" usage of them (like "q=0.9", "q=0.8" and so on), but it only works
* if there is a maximum of 28 "Accept" headers. If we have more than that (which is extremely unlikely), then we fall back to a 1-by-1
* decrement rule, which will result in quality codes like "q=0.999", "q=0.998" etc.
*
* @param int $currentWeight varying from 1 to 1000 (will be divided by 1000 to build the quality value)
* @param bool $hasMoreThan28Headers
* @return int
*/
public function getNextWeight(int $currentWeight, bool $hasMoreThan28Headers): int
{
if ($currentWeight <= 1) {
return 1;
}
if ($hasMoreThan28Headers) {
return $currentWeight - 1;
}
return $currentWeight - 10 ** floor( log10($currentWeight - 1) );
}
}