01.Le Contexte
Des lenteurs récurrentes sur des transactions SAP (recherche produits, validation commandes) impactaient des milliers d'utilisateurs quotidiens. Les équipes suspectaient le réseau/Wi-Fi, mais les métriques bas-niveau montraient des temps de transit < 50 ms.
Objectif : isoler la vraie cause racine sans accès direct au backend SAP.
02.Analyse & Preuve par l'Image
Dashboards Power BI construits à partir des logs du robot PowerShell. Chaque transaction est mesurée finement : connectTime, sslTime, firstByteTime, lastByteTime, SapPerf2 (temps serveur via header HTTP).
Slide 1 – Vue globale : pic clair à 154 s sur transaction 8 (premier octet serveur). Preuve que le réseau n'est pas en cause (transit stable < 50 ms).
Slide 2 – Zoom Recherche Produits (index 4) : lenteurs applicatives (rectangles jaunes) vs réseau (rouges). Dichotomie par index unique pour isoler rapidement les pages fautives.
Slide 3 – Retour commandes (index 8) : SapPerf2 extrême (> 150 s), preuve formelle d'un défaut backend SAP (SQL générée inefficace).
03. Ingénierie "Raw Socket"
Développement d'un robot PowerShell custom pour reconstruire la stack HTTP/HTTPS/TLS sans dépendre de bibliothèques haut-niveau.
Note : L'architecture repose sur deux fonctions distinctes : une pour générer la requête, et une seconde pour gérer son envoi, ce qui facilite l'extension à d'autres protocoles.
function connectSyn
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="Hostname or IPAdresse")]
[string]$serverName,
[Parameter(Mandatory=$true,
HelpMessage="port")]
[int]$port,
[Parameter(Mandatory=$false,
HelpMessage="stateObject")]
$stateObject
)
$sw = [Diagnostics.StopWatch]::StartNew()
[System.Net.Sockets.TcpClient]$stateObject.socket = new-object System.Net.Sockets.TcpClient($serverName, $port)
[int]$stateObject.connectTime = $sw.ElapsedMilliseconds
$stateObject.socket.ReceiveTimeout = 30000
$stateObject.socket.SendTimeout = 30000
$sw.Stop()
$sw = $null
return $stateObject
}
function closeSocket
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,
HelpMessage="closeSocket")]
[bool]$close = $true,
[Parameter(Mandatory=$true,
HelpMessage="stateObject")]
$stateObject
)
if($close)
{
if($stateObject.sslStream)
{
$stateObject.sslStream.Close()
$stateObject.sslStream.Dispose()
}
if($stateObject.socket)
{
$stateObject.socket.Close()
$stateObject.socket.Dispose()
}
}
return $stateObject
}
function sslAuthenticate
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="Hostname or IPAdresse")]
[string]$serverName,
[Parameter(Mandatory=$false,
HelpMessage="stateObject")]
$stateObject
)
[System.Net.Sockets.NetworkStream]$stream = $stateObject.socket.GetStream()
$stream.WriteTimeout = 30000
$stream.ReadTimeout = 30000
[System.Net.Security.SslStream]$stateObject.sslStream = New-Object System.Net.Security.SslStream($stream, $true, [System.Net.Security.RemoteCertificateValidationCallback]{$true})
$stateObject.sslStream.ReadTimeout = 30000
$stateObject.sslStream.WriteTimeout = 30000
$ccCol = New-Object System.Security.Cryptography.X509Certificates.X509CertificateCollection
$sw = [Diagnostics.StopWatch]::StartNew()
$stateObject.sslStream.AuthenticateAsClient($serverName,$ccCol,[System.Security.Authentication.SslProtocols]::Tls12,$false)
[int]$stateObject.sslTime = $sw.ElapsedMilliseconds
$sw.Stop()
$sw = $null
return $stateObject
}
function HTTP
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="request")]
[string]$request,
[Parameter(Mandatory=$false,
HelpMessage="stateObject")]
$stateObject
)
$encodingUTF8 = New-Object System.Text.UTF8Encoding
$encodingASCII = New-Object System.Text.ASCIIEncoding
$buffer = New-Object System.Byte[] 1500
[int]$i = -1
[int]$read = -1
[int]$decalage = -1
[int]$pagesize = 0
[int]$Length = 0
$stream = $stateObject.socket.GetStream()
$streamWriter = New-Object System.IO.StreamWriter($stream)
$stream.WriteTimeout = 30000
$stream.ReadTimeout = 30000
$streamWriter.Write($request)
$streamWriter.Flush()
$sw = [Diagnostics.StopWatch]::StartNew()
while($decalage -lt $Length -and $read -ne 0)
{
[int]$read = $stream.Read($buffer, 0, $buffer.Length)
$pagesize += $read
$responseFromServer += $encodingUTF8.GetString($buffer, 0, $read)
if ($i -le 0)
{
if ($i -eq -1)
{
[int]$stateObject.firstByte = $sw.ElapsedMilliseconds;
$sw.Restart();
}
$isContent = isfindHeader $responseFromServer
if($isContent -eq $false){$i = -1}
else {$Length = findContentLength $responseFromServer; $decalage =
beginBuffer $responseFromServer;}
$read = -1
}
$i++
if($read -ge 0){$decalage += $read}
}
[int]$stateObject.lastByte = $sw.ElapsedMilliseconds
[int]$stateObject.pagesize = $pagesize
[int]$stateObject.contentLength = $Length
[int]$stateObject.downloadLength = $decalage
[int]$stateObject.responseCode = $responseFromServer.Substring(9, 3)
$tmpTime = returnTimeConnect $stateObject.connectTime $stateObject.sslTime
[int]$stateObject.total = $stateObject.firstByte + $stateObject.lastByte + $tmpTime
$stateObject.responseFromServer = $responseFromServer
if($stateObject.downloadLength -gt 0 -and $stateObject.lastByte -gt 0)
{ [int]$stateObject.debitMoyen = ($stateObject.downloadLength / $stateObject.lastByte * 1000) }
else
{ [int]$stateObject.debitMoyen = 0 }
$sw.Stop()
$sw = $null
return $stateObject
}
function HTTPS
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="request")]
[string]$request,
[Parameter(Mandatory=$false,
HelpMessage="stateObject")]
$stateObject
)
$encodingUTF8 = New-Object System.Text.UTF8Encoding
$encodingASCII = New-Object System.Text.ASCIIEncoding
$buffer = New-Object System.Byte[] 1500
[int]$i = -1
[int]$read = -1
[int]$decalage = -1
[int]$pagesize = 0
[int]$Length = 0
$encodeBytes = $encodingASCII.GetBytes($request)
$stateObject.sslStream.Write($encodeBytes)
$stateObject.sslStream.Flush()
$sw = [Diagnostics.StopWatch]::StartNew()
while($decalage -lt $Length -and $read -ne 0)
{
[int]$read = $stateObject.sslStream.Read($buffer, 0, $buffer.Length)
$pagesize += $read
$responseFromServer += $encodingUTF8.GetString($buffer, 0, $read)
if ($i -le 0)
{
if ($i -eq -1)
{
[int]$stateObject.firstByte = $sw.ElapsedMilliseconds;
$sw.Restart();
}
$isContent = isfindHeader $responseFromServer
if($isContent -eq $false){$i = -1}
else {$Length = findContentLength $responseFromServer; $decalage =
beginBuffer $responseFromServer;}
$read = -1
}
$i++
if($read -ge 0){$decalage += $read}
}
[int]$stateObject.lastByte = $sw.ElapsedMilliseconds
[int]$stateObject.pagesize = $pagesize
[int]$stateObject.contentLength = $Length
[int]$stateObject.downloadLength = $decalage
[int]$stateObject.responseCode = $responseFromServer.Substring(9, 3)
$tmpTime = returnTimeConnect $stateObject.connectTime $stateObject.sslTime
[int]$stateObject.total = $stateObject.firstByte + $stateObject.lastByte + $tmpTime
$stateObject.responseFromServer = $responseFromServer
$stateObject.sapperf = sapPerf $responseFromServer
if($stateObject.downloadLength -gt 0 -and $stateObject.lastByte -gt 0)
{ [int]$stateObject.debitMoyen = ($stateObject.downloadLength / $stateObject.lastByte * 1000) }
else
{ [int]$stateObject.debitMoyen = 0 }
$sw.Stop()
$sw = $null
return $stateObject
}
function isfindHeader($responseFromServer)
{
if ($responseFromServer.Contains("`r`n`r`n"))
{
return $true
}
return $false
}
function beginBuffer($responseFromServer)
{
if ($responseFromServer.Contains("`r`n`r`n"))
{
[int]$indexOf = $responseFromServer.IndexOf("`r`n`r`n")
[int]$decalage = $responseFromServer.Length - $indexOf - 4
}
return $decalage
}
function findContentLength($responseFromServer)
{
[int]$Length = 0
$tmp = $responseFromServer -split "`r`n"
foreach($header in $tmp)
{
if($header.Contains("Content-Length"))
{
$Length = $header -replace "Content-Length: ", ""
}
if($header.Contains("content-length"))
{
$Length = $header -replace "content-length: ", ""
}
if($header.Contains("Transfer-Encoding: chunked"))
{
$Length = 70000
}
}
return $Length
}
function sapPerf($responseFromServer)
{
if ($responseFromServer -match 'sap-perf-fesrec: (\d+)')
{
return $Matches[1]
}
return -1
}
function Invoke-RequestWeb
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="Hostname or IPAdresse")]
[string]$serverName,
[Parameter(Mandatory=$true,
HelpMessage="uri")]
[string]$uri,
[Parameter(Mandatory=$false,
HelpMessage="fulluri")]
[string]$fulluri,
[Parameter(Mandatory=$false,
HelpMessage="method")]
[string]$method = "GET",
[Parameter(Mandatory=$false,
HelpMessage="cookie")]
[string]$cookie = $null,
[Parameter(Mandatory=$false,
HelpMessage="keep-alive")]
[bool]$keepalive = $false,
[Parameter(Mandatory=$false,
HelpMessage="referer")]
[string]$referer,
[Parameter(Mandatory=$false,
HelpMessage="bodyPost")]
[string]$bodyPost = $null,
[Parameter(Mandatory=$false,
HelpMessage="bodyPost")]
[int]$contentlength = 0,
[Parameter(Mandatory=$false,
HelpMessage="accept")]
[string]$accept = $null,
[Parameter(Mandatory=$false,
HelpMessage="accept")]
[string]$useragent = $null,
[Parameter(Mandatory=$false,
HelpMessage="contenttype")]
[string]$contenttype = $null,
[Parameter(Mandatory=$false,
HelpMessage="secfetch")]
[string]$secfetch = $null,
[Parameter(Mandatory=$false,
HelpMessage="maxage")]
[string]$maxage = $null,
[Parameter(Mandatory=$false,
HelpMessage="upgradesecure")]
[string]$upgradesecure = $null,
[Parameter(Mandatory=$false,
HelpMessage="authobasic")]
[string]$authobasic = $null,
[Parameter(Mandatory=$false,
HelpMessage="xmlRequest")]
[bool]$xmlRequest=$false,
[Parameter(Mandatory=$false,
HelpMessage="proxy")]
[bool]$proxy,
[Parameter(Mandatory=$false,
HelpMessage="user")]
[string]$user,
[Parameter(Mandatory=$false,
HelpMessage="password")]
[string]$password,
[Parameter(Mandatory=$false,
HelpMessage="domaine")]
[string]$domaine,
[Parameter(Mandatory=$false,
HelpMessage="authNTLM")]
[bool]$authNTLM = $false,
[Parameter(Mandatory=$false,
HelpMessage="authNTLMResponse")]
[string]$authNTLMResponse = $null
)
$text = ("$method $uri HTTP/1.1`r`n" +
"Host: $serverName`r`n")
if ($keepalive) {$text += "Connection: keep-alive`r`n"}
else {$text += "Connection: close`r`n"}
if ($accept.Length -gt 0) {$text += "Accept: $accept`r`n"}
else {$text += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8`r`n"}
if ($useragent.Length -gt 0){$text += "User-Agent: $useragent`r`n"}
else {$text += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0`r`n"}
if ($bodyPost.Length -gt 0){$text += "Content-Length: $($bodyPost.Length)`r`n"}
if($contentlength -gt 0) {$text += "Content-Length: $contentlength`r`n"}
if ($xmlRequest){$text += "X-Requested-With: XMLHttpRequest`r`n"}
if($contenttype) {$text += "Content-Type: application/x-www-form-urlencoded`r`n"}
if($authobasic.Length -gt 0){$text += "Authorization: Basic $authobasic`r`n"}
$text += "Accept-Encoding: br`r`n"
$text += "Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3`r`n"
if ($cookie.Length -gt 0) {$text += "$cookie`r`n"}
if($maxage){$text += "Cache-Control: max-age=0`r`n"}
if($secfetch){$text += "Sec-Fetch-Dest: document`r`nSec-Fetch-Mode: navigate`r`nSec-Fetch-Site: none`r`n"}
if($upgradesecure){$text += "Upgrade-Insecure-Requests: 1`r`n"}
if($authoNTLM)
{
$obj = [System.Net.Ntlm]::new($user, $password, $domaine)
if(!$authNTLMexecute)
{
$data = $obj.CreateNegotiateMessage()
$text += "Authorization: $data`r`n"
}
else
{
$data = $obj.ProcessChallenge($authNTLMResponse)
$text += "Authorization: $data`r`n"
}
}
$text += "`r`n"
if ($bodyPost.Length -gt 0) {$text += $bodyPost}
return $text
}
function Invoke-NetworkWeb
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="Hostname or IPAdresse")]
[string]$serverName,
[Parameter(Mandatory=$true,
HelpMessage="port")]
[int]$port,
[Parameter(Mandatory=$false,
HelpMessage="proxy")]
[string]$proxy = $null,
[Parameter(Mandatory=$true,
HelpMessage="text")]
[string]$request,
[Parameter(Mandatory=$false,
HelpMessage="ssl")]
[bool]$ssl = $false,
[Parameter(Mandatory=$false,
HelpMessage="stateObject")]
[Hashtable]$stateObject,
[Parameter(Mandatory=$false,
HelpMessage="retry")]
[int]$retry = 0
)
[uri]$checkProxy = if($proxy.Length -gt 0) {decodeProxy $proxy}
[bool]$close = $false
if($request.Contains("Connection: close"))
{
$close = $true
}
$stateObject.serverName = $serverName
$stateObject.port = $port
$stateObject.method = findMethod $request
$stateObject.uri = findUri $request
$stateObject.urivariable = findUriVariable $request
$stateObject.variable = findvariable $request
$stateObject.retry = $retry
$stateObject.startDateHours = get-date -Format "dd/MM/yyyy HH:00:00"
$stateObject.startDateFiveMinutes = dateFiveMinute
$stateObject.startDateMinutes = get-date -Format "dd/MM/yyyy HH:mm:00"
$stateObject.startDateSeconds = get-date -Format "dd/MM/yyyy HH:mm:ss"
$stateObject.startDate = get-date -Format "dd/MM/yyyy HH:mm:ss.fff"
try
{
if (!$stateObject.socket.Connected)
{
if($checkProxy.IsAbsoluteUri)
{
$stateObject = connectSyn $checkProxy.Authority $checkProxy.Port $stateObject
}
else
{
$stateObject = connectSyn $serverName $port $stateObject
if($stateObject.port -eq "443" -or $ssl)
{
$stateObject = sslAuthenticate $serverName $stateObject
}
}
}
if($stateObject.socket.Connected)
{
$stateObject = if($stateObject.port -eq "443" -or $ssl){HTTPS $request $stateObject}
else{HTTP $request $stateObject}
$stateObject = closeSocket $close $stateObject
}
}
catch
{
New-Log($_)
$stateObject.error = 1
$stateObject.endDate=get-date -Format "dd/MM/yyyy HH:mm:ss.fff"
if($retry -eq 3)
{
return $stateObject
}
else
{
Start-Sleep -Seconds 3
$retry = $retry + 1
$stateObject = closeSocket $close $stateObject
Write-Host "[Retry]" -NoNewline -ForegroundColor Gray ; Write-Host " Nombre de retry : $($retry)"
$stateObject = Invoke-NetworkWeb -serverName $serverName -port $port -request $request -stateObject $stateObject -retry $retry
}
return $stateObject
}
$stateObject.endDate=get-date -Format "dd/MM/yyyy HH:mm:ss.fff"
return $stateObject
}
function Invoke-RequestWeb
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="Hostname or IPAdresse")]
[string]$serverName,
[Parameter(Mandatory=$true,
HelpMessage="uri")]
[string]$uri,
[Parameter(Mandatory=$false,
HelpMessage="fulluri")]
[string]$fulluri,
[Parameter(Mandatory=$false,
HelpMessage="method")]
[string]$method = "GET",
[Parameter(Mandatory=$false,
HelpMessage="cookie")]
[string]$cookie = $null,
[Parameter(Mandatory=$false,
HelpMessage="keep-alive")]
[bool]$keepalive = $false,
[Parameter(Mandatory=$false,
HelpMessage="referer")]
[string]$referer,
[Parameter(Mandatory=$false,
HelpMessage="bodyPost")]
[string]$bodyPost = $null,
[Parameter(Mandatory=$false,
HelpMessage="bodyPost")]
[int]$contentlength = 0,
[Parameter(Mandatory=$false,
HelpMessage="accept")]
[string]$accept = $null,
[Parameter(Mandatory=$false,
HelpMessage="accept")]
[string]$useragent = $null,
[Parameter(Mandatory=$false,
HelpMessage="contenttype")]
[string]$contenttype = $null,
[Parameter(Mandatory=$false,
HelpMessage="secfetch")]
[string]$secfetch = $null,
[Parameter(Mandatory=$false,
HelpMessage="maxage")]
[string]$maxage = $null,
[Parameter(Mandatory=$false,
HelpMessage="upgradesecure")]
[string]$upgradesecure = $null,
[Parameter(Mandatory=$false,
HelpMessage="authobasic")]
[string]$authobasic = $null,
[Parameter(Mandatory=$false,
HelpMessage="xmlRequest")]
[bool]$xmlRequest=$false,
[Parameter(Mandatory=$false,
HelpMessage="proxy")]
[bool]$proxy,
[Parameter(Mandatory=$false,
HelpMessage="user")]
[string]$user,
[Parameter(Mandatory=$false,
HelpMessage="password")]
[string]$password,
[Parameter(Mandatory=$false,
HelpMessage="domaine")]
[string]$domaine,
[Parameter(Mandatory=$false,
HelpMessage="authNTLM")]
[bool]$authNTLM = $false,
[Parameter(Mandatory=$false,
HelpMessage="authNTLMResponse")]
[string]$authNTLMResponse = $null
)
$text = ("$method $uri HTTP/1.1`r`n" +
"Host: $serverName`r`n")
if ($keepalive) {$text += "Connection: keep-alive`r`n"}
else {$text += "Connection: close`r`n"}
if ($accept.Length -gt 0) {$text += "Accept: $accept`r`n"}
else {$text += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8`r`n"}
if ($useragent.Length -gt 0){$text += "User-Agent: $useragent`r`n"}
else {$text += "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0`r`n"}
if ($bodyPost.Length -gt 0){$text += "Content-Length: $($bodyPost.Length)`r`n"}
if($contentlength -gt 0) {$text += "Content-Length: $contentlength`r`n"}
if ($xmlRequest){$text += "X-Requested-With: XMLHttpRequest`r`n"}
if($contenttype) {$text += "Content-Type: application/x-www-form-urlencoded`r`n"}
if($authobasic.Length -gt 0){$text += "Authorization: Basic $authobasic`r`n"}
$text += "Accept-Encoding: br`r`n"
$text += "Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3`r`n"
if ($cookie.Length -gt 0) {$text += "$cookie`r`n"}
if($maxage){$text += "Cache-Control: max-age=0`r`n"}
if($secfetch){$text += "Sec-Fetch-Dest: document`r`nSec-Fetch-Mode: navigate`r`nSec-Fetch-Site: none`r`n"}
if($upgradesecure){$text += "Upgrade-Insecure-Requests: 1`r`n"}
if($authoNTLM)
{
$obj = [System.Net.Ntlm]::new($user, $password, $domaine)
if(!$authNTLMexecute)
{
$data = $obj.CreateNegotiateMessage()
$text += "Authorization: $data`r`n"
}
else
{
$data = $obj.ProcessChallenge($authNTLMResponse)
$text += "Authorization: $data`r`n"
}
}
$text += "`r`n"
if ($bodyPost.Length -gt 0) {$text += $bodyPost}
return $text
}
function replaceCookie($serverName, $cookieContainer, $updcookie)
{
foreach($cookie in $cookieContainer)
{
if ($cookie.name -eq $updcookie.name -and $serverName -eq $cookie.servername)
{
$cookie.value = $updcookie.value
break
}
}
return $cookieContainer
}
function existeCookie($serverName, $cookieContainer, $udpcookie)
{
foreach($cookie in $cookieContainer)
{
if ($cookie.name -eq $udpcookie.name -and $serverName -eq $cookie.servername)
{
return 1
}
}
return 0
}
function saveCookie($serverName, $responseFromServer, [array]$cookieContainer)
{
$tmp = $responseFromServer -split "`r`n`r`n"
$headers = $tmp[0] -split "`r`n"
foreach($header in $headers)
{
if ($header -match "Set-Cookie")
{
$cookieString = $header -replace "Set-Cookie: ", ""
$cookieTmp = $cookieString -split ';'
$cookie = @{}
$cookie.servername = $serverName
$cookie.name = $cookieTmp[0].Substring(0, $cookieTmp[0].IndexOf('=')).Trim()
$cookie.value = $cookieTmp[0].Substring($cookieTmp[0].IndexOf('=') + 1, ($cookieTmp[0].Length - $cookieTmp[0].IndexOf('=') -1)).Trim()
if ($cookie.value -eq "") {$cookie.value = $null}
if ($cookieContainer.Count -eq 0)
{
$cookieContainer += $cookie
}
else
{
$check = existeCookie $serverName $cookieContainer $cookie
if (!$check) {$cookieContainer += $cookie}
else {$cookieContainer = replaceCookie $serverName $cookieContainer $cookie}
}
}
}
return $cookieContainer
}
function createCookie($serverName, $cookieContainer, $liste)
{
$newCookieString = "Cookie: "
foreach($object in $liste)
{
foreach($cookie in $cookieContainer)
{
if ($cookie.name -match $object -and $serverName -eq $cookie.servername)
{
$newCookieString = "{0}{1}={2}; " -f $newCookieString, $cookie.name, $cookie.value
}
}
}
$newCookieString = $newCookieString.Substring(0, $newCookieString.Length -2)
return $newCookieString
}
function initStateObject()
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,
HelpMessage="stateObject")]
[Hashtable]$oldStateObject
)
[Hashtable]$stateObject = @{}
if ($oldStateObject.Count -gt 0)
{
$stateObject.socket = $oldStateObject.socket
$stateObject.sslStream = $oldStateObject.sslStream
}
else
{
[System.Net.Sockets.TcpClient]$stateObject.socket = $null
[System.Net.Security.SslStream]$stateObject.sslStream = $null
}
[int]$stateObject.index = -1
[int]$stateObject.port = $null
[int]$stateObject.connectTime = $null
[int]$stateObject.sslTime = $null
[int]$stateObject.firstByte = $null
[int]$stateObject.lastByte = $null
[int]$stateObject.pagesize = $null
[int]$stateObject.contentLength = 0
[int]$stateObject.downloadLength = 0
[int]$stateObject.debitMoyen = $null
[int]$stateObject.responseCode = $null
[int]$stateObject.total = $null
[int]$stateObject.check = 0
[int]$stateObject.error = 0
[int]$stateObject.sapperf = 0
[int]$stateObject.retry = 0
$stateObject.startDate = "null"
$stateObject.startDateHours = "null"
$stateObject.startDateFiveMinutes = "null"
$stateObject.startDateMinutes = "null"
$stateObject.startDateSeconds = "null"
$stateObject.endDate = "null"
$stateObject.message = "null"
$stateObject.method = "null"
$stateObject.responseFromServer = "null"
$stateObject.serverName = "null"
$stateObject.name = $global:name
$stateObject.uri = "null"
$stateObject.urivariable = "null"
$stateObject.variable = "null"
$stateObject.ntlmChallenge = "null"
$stateObject.log = "null"
return [Hashtable]$stateObject
}
function Network()
{
$mail = htmlencode $SAP.mail
$password = convertToSecureString $SAP.password
$responseTime = @()
$cookieContainer = @()
$tokenRand = $null
$serverName = "le_serveur_sap.com"
[int]$requestCount = 1
$XSRF = $null
#### Première requête, /
$stateObject = initStateObject
$requestConn = Invoke-RequestWeb -serverName $serverName -uri "/qs1_120" -method "GET"
$stateObject = Invoke-NetworkWeb -serverName $serverName -port 443 -request $requestConn -stateObject $stateObject
$stateObject.index = $requestCount++
if ($stateObject.responseCode -eq 200)
{
$cookieContainer = saveCookie $serverName $stateObject.responseFromServer $cookieContainer
$XSRF = findXSRF $stateObject.responseFromServer
$stateObject.check = 1
}
$stateObject.responseFromServer = encodeBase64 $stateObject.responseFromServer
$responseTime += $stateObject
$location = $null
#### deuxième requête ### /Authentification
$dataPost = "sap-system-login-oninputprocessing=onLogin&sap-urlscheme=&sap-system-login=onLogin&sap-system-login-basic_auth=&sap-client=120&sap-accessibility=&sap-login-XSRF=$XSRF&sap-system-login-cookie_disabled&sap-hash=&sap-user=$($mail)&sap-password=$($password)&sap-language=FR"
$stateObject = initStateObject
$liste = @("sap-")
$cookieString = createCookie $serverName $cookieContainer $liste
$requestAuth = Invoke-RequestWeb -contenttype $true -serverName $serverName -uri "/qs1_120" -method "POST" -cookie $cookieString -bodyPost $dataPost
$stateObject = Invoke-NetworkWeb -serverName $serverName -port 443 -request $requestAuth -stateObject $stateObject
$stateObject.index = $requestCount++
if($stateObject.responseCode -eq 302)
{
$cookieContainer = saveCookie $serverName $stateObject.responseFromServer $cookieContainer
$location = findLocation $stateObject.responseFromServer
$stateObject.check = 1
}
$stateObject.responseFromServer = encodeBase64 $stateObject.responseFromServer
$responseTime += $stateObject
}
function auto($polling, $exectime)
{
$sleep = getStartTime
$endExecution = getEndTime $execTime
if($execTime -ne 0)
{
Write-Host "[INFO]" -NoNewline -ForegroundColor Yellow; Write-Host " Fin d'exécution du script à $endExecution"
}
Write-Host "[Sync]" -NoNewline -ForegroundColor Green; Write-Host " En cours de synchronisation..."
Write-Host " Temps restant $sleep secondes."
Start-Sleep -Seconds $sleep
while($true)
{
Write-Host ""
$dateSystem = Get-Date
if(HeuresOuvres $dateSystem)
{
Write-Host "[Depart]" -NoNewline -ForegroundColor Green; Write-Host " Date : $dateSystem"
$responseTime = network
$responseTime
}
$scnSleep = getStartTime
Write-Host "[Info]" -NoNewline -ForegroundColor Yellow; Write-Host " SLEEP = $scnSleep secondes"
Start-sleep -seconds $scnSleep
}
}
04.Méthodologie & Impact
- Récupération traces réseau + mesure bas-niveau via robot custom.
- Définition d'un scénario de reproduction, reverse engineering des échanges HTTP, prise en compte de l'authentification et des requêtes multiples.
- Visualisation Power BI : corrélation entre les temps réseau et les temps de traitement serveur (SapPerf2).
- Mise en évidence factuelle : transit réseau < 50 ms vs traitement serveur > 150 s.
- Reconnaissance par l'éditeur SAP d'un problème de performance lié aux requêtes SQL.
- Impact : Réduction drastique du temps d'analyse d'incidents (jours → heures), justification pour correctif éditeur.
- Outil : Script actuellement exploité sur d'autres projets.