Page 1 of 2
Simple HTTP Server crashes on linux/mac on stress test
Posted: Mon Dec 22, 2025 10:04 pm
by skinkairewalker
I’m sharing a simple HTTP server I wrote in PureBasic. The server is very minimal, based on CreateNetworkServer() and a loop that continuously calls NetworkServerEvent() to accept connections, spawning a new thread to handle each request. On Windows, the server behaves correctly and remains stable even under stress testing with many simultaneous connections. However, on Linux and macOS, running the same stress test causes the application to crash after some time for an unknown reason. The crash seems to happen around the call to NetEvent = NetworkServerEvent(), resulting in a silent termination or, in some cases, a segmentation fault. I’ve verified that connections are being closed properly and that the number of threads is limited, but the issue still occurs only on Linux and macOS, not on Windows. I’m trying to understand whether this is a platform-specific limitation, a threading issue, or a known problem with NetworkServerEvent() on these systems.
Threaded version (non-professional)
simplehttpwebview.pb
Code: Select all
EnableExplicit
#PORT = 8080
#WWW = "www/"
Global event
Structure ClientCtx
id.i
request.s
EndStructure
;-----------------------------------------
Procedure.s GetMimeType(file.s)
Select LCase(GetExtensionPart(file))
Case "html","htm" : ProcedureReturn "text/html"
Case "js" : ProcedureReturn "application/javascript"
Case "css" : ProcedureReturn "text/css"
Case "png" : ProcedureReturn "image/png"
Case "jpg","jpeg" : ProcedureReturn "image/jpeg"
Case "gif" : ProcedureReturn "image/gif"
EndSelect
ProcedureReturn "application/octet-stream"
EndProcedure
;-----------------------------------------
Procedure HandleClient(*ctx.ClientCtx);HandleClient(client)
Protected client = *ctx\id
Protected request.s = *ctx\request
Debug "cliente = "+Str(client)
;Debug "request = "+request
Protected *req, *buf
Protected path.s, file.s
Protected size.q, sent, bytesRead, n.q
Protected fn, mime.s
Protected header.s
Protected TotalEnviado.q = 0
Protected fileSize.q
path = StringField(StringField(request, 1, #CRLF$), 2, " ")
If (FindString(path,"?"))
path = StringField(path, 1, "?")
EndIf
If path = "/" : path = "/index.html" : EndIf
Debug "PATH === "+path
file = #WWW + Mid(path, 2)
If FileSize(file) >= 0
fn = ReadFile(#PB_Any, file)
size = Lof(fn)
mime = GetMimeType(file)
header = "HTTP/1.1 200 OK" + #CRLF$ +
"Content-Type: " + mime + #CRLF$ +
"Content-Length: " + Str(size) + #CRLF$ +
"Connection: close" + #CRLF$ +
#CRLF$
SendNetworkString(client, header, #PB_UTF8)
; ===== STREAMING =====
;*buf = AllocateMemory(16384)
*buf = AllocateMemory(16384)
fileSize = Lof(fn)
While TotalEnviado < fileSize And Not Eof(fn)
bytesRead = ReadData(fn, *buf, 16384 )
If bytesRead <= 0
Break
EndIf
sent = 0
While sent < bytesRead
n = SendNetworkData(client, *buf + sent, bytesRead - sent)
If n > 0
sent + n
TotalEnviado + n
ElseIf n = -1
; Socket ocupado, espera um pouco
Delay(1)
Else
Debug "Cliente fechou a conexão"
Break 2
EndIf
Wend
; Debug "Chunk enviado: " + Str(sent)
; Debug "Total enviado: " + Str(TotalEnviado) + " / " + Str(fileSize)
Wend
FreeMemory(*buf)
CloseFile(fn)
Debug "ENVIO FINALIZADO: " + Str(TotalEnviado)
Else
header = "HTTP/1.1 404 Not Found" + #CRLF$ +
"Content-Length: 13" + #CRLF$ +
"Connection: close" + #CRLF$ +
#CRLF$ +
"404 Not Found"
SendNetworkString(client, header, #PB_UTF8)
EndIf
;Debug "Conexão Encerrada"
;CloseNetworkConnection(client)
FreeStructure(*ctx)
EndProcedure
Procedure ServerThread(void)
;-----------------------------------------
Protected NetEvent, client
Repeat
NetEvent = NetworkServerEvent()
If NetEvent
client = EventClient()
If NetEvent = #PB_NetworkEvent_Data
;HandleClient(client)
;Debug "################# DADOS recebidos ######################"
Protected *ctx.ClientCtx = AllocateStructure(ClientCtx)
Protected *buffer = AllocateMemory(4096)
Protected result
*ctx\id = client
Repeat
FillMemory(*buffer, 4096, 0)
result = ReceiveNetworkData(client, *buffer, 4096)
If result > 0
*ctx\request + PeekS(*buffer, result, #PB_UTF8)
EndIf
Until result < 4096
FreeMemory(*buffer)
; AGORA o socket está “isolado”
CreateThread(@HandleClient(), *ctx)
ElseIf NetEvent = #PB_NetworkEvent_Disconnect
; Debug "DESCONECTADO DO SERVIDOR"
EndIf
Else
Delay(1) ; Ne pas saturer le CPU / Don't stole the whole CPU !
EndIf
ForEver
EndProcedure
If CreateNetworkServer(0, #PORT,#PB_Network_TCP|#PB_Network_IPv4|#PB_Network_IPv6)
Debug "Servidor em http://localhost:" + Str(#PORT)
CreateThread(@ServerThread(),0)
If OpenWindow(0, 0, 0, 800, 600, "AppComposer", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget)
If CreateMenu(0, WindowID(0)) ; Create a regular menu with title and one item
MenuTitle("File")
MenuItem(1, "Reload" + #TAB$ + "Ctrl+O")
EndIf
WebViewGadget(0, 0, 0, 800, 600)
SetGadgetText(0, "http://localhos:8080")
HideWindow(0, #False)
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Menu
Select EventMenu()
Case 1
SetGadgetText(0, "http://localhost:8080")
EndSelect
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
EndIf
Else
End
EndIf
stress_test.pb
Code: Select all
Global port = 8080
#Loadtest=01
Global semtest = CreateSemaphore()
Procedure _loadLest(amount)
Protected get.s, rec.s,con,a,try=1
rec = "Host: 192.168.3.91:" + Str(port) + #CRLF$
rec + "Connection: keep-alive" + #CRLF$
rec + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0" + #CRLF$
rec + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" + #CRLF$
rec + "Sec-Fetch-Site: none" + #CRLF$
rec + "Sec-Fetch-Mode: navigate" + #CRLF$
rec + "Sec-Fetch-User: ?1" + #CRLF$
rec + "Sec-Fetch-Dest: document" + #CRLF$
rec + "Accept-Encoding: gzip, deflate, br" + #CRLF$
rec + "Accept-Language: en-US,en;q=0.9" + #CRLF$ + #CRLF$
WaitSemaphore(semtest)
Repeat
con = OpenNetworkConnection("192.168.3.91",port,#PB_Network_TCP | #PB_Network_IPv4,1000)
If con
get = "GET / HTTP/1.1" + #CRLF$
SendNetworkString(Con,get+rec)
Delay(100)
get = "GET /favicon.ico HTTP/1.1" + #CRLF$
SendNetworkString(Con,get+rec)
Debug "Sent on try " + Str(try)
Break
Else
Debug "can't connect retry " + Str(try)
Delay(10)
try+1
EndIf
Until try > 5
If try > 5
Debug "couldn't connect"
EndIf
EndProcedure
Procedure loadTest(amount)
Protected a
Dim threads(amount)
For a = 1 To amount
threads(a) = CreateThread(@_loadLest(),0)
Next
Delay(1000)
Debug "signal"
For a = 1 To amount
SignalSemaphore(semtest)
Delay(1)
Next
For a = 1 To amount
If IsThread(threads(a))
WaitThread(threads(a),1000)
EndIf
Next
Debug "done test"
EndProcedure
loadTest(10000)
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 23, 2025 1:26 am
by mk-soft
ThreadSafe executable active?
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 23, 2025 2:08 am
by skinkairewalker
mk-soft wrote: Tue Dec 23, 2025 1:26 am
ThreadSafe executable active?
yep
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 23, 2025 9:51 am
by mk-soft
You should create a map list (ConncectionID) per client with a structure Size, Data Buffer.
After ReceiveNetworkData, read the size from the header, take over the data and wait for the next #PB_NetworkEvent_Data to receive the current or the next client.
Only when after reaching size data call the client thread.
If SendNetworkData is missing, you must cancel. Or read the NetworkError and then decide whether the sending buffer is full or the connection has been closed.
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 23, 2025 10:57 am
by infratec
Also you have a bug in:
Code: Select all
*ctx\request + PeekS(*buffer, result, #PB_UTF8)
It has to be:
Code: Select all
*ctx\request + PeekS(*buffer, result, #PB_UTF8|#PB_ByteLength)
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Mon Dec 29, 2025 7:23 pm
by skinkairewalker
infratec wrote: Tue Dec 23, 2025 10:57 am
Also you have a bug in:
Code: Select all
*ctx\request + PeekS(*buffer, result, #PB_UTF8)
It has to be:
Code: Select all
*ctx\request + PeekS(*buffer, result, #PB_UTF8|#PB_ByteLength)
fixed xD, thanks
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Mon Dec 29, 2025 7:25 pm
by skinkairewalker
mk-soft wrote: Tue Dec 23, 2025 9:51 am
You should create a map list (ConncectionID) per client with a structure Size, Data Buffer.
After ReceiveNetworkData, read the size from the header, take over the data and wait for the next #PB_NetworkEvent_Data to receive the current or the next client.
Only when after reaching size data call the client thread.
If SendNetworkData is missing, you must cancel. Or read the NetworkError and then decide whether the sending buffer is full or the connection has been closed.
I tried to do it here, but with the little knowledge I have I ended up breaking the code xD. Is that the only way to solve the problem? I even asked ChatGPT for help, but it couldn’t help me either.
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Mon Dec 29, 2025 10:22 pm
by infratec
You can try this:
Code: Select all
;
; as stress test use curl >= 8.16 https://curl.se/download.html
;
; curl -6 --parallel --parallel-immediate --parallel-max-host 1000 -o nul "http://localhost:8080/portmon.exe?[1-1000]"
; curl -4 -k --parallel --parallel-immediate --parallel-max-host 1000 -o nul "https://localhost:8443/portmon.exe?[1-1000]"
;
; linux/macOS: use -o /dev/null instead of -o nul
;
CompilerIf Not #PB_Compiler_Thread
CompilerError "You have to enable thread safe in compiler options"
CompilerEndIf
EnableExplicit
#HTTP_PORT = 8080
#HTTPS_PORT = 8443
#WWW = "www"
Structure WebServerClient_Structure
Client.i
Thread.i
*Buffer
Size.i
LastSeen.i
Disconnected.i
RootPath$
EndStructure
Structure WebServer_Structure
Thread.i
Port.i
ServerFlags.i
PrivateKey$
Certificate$
CACertificate$
Server.i
RootPath$
Map ClientMap.WebServerClient_Structure()
Exit.i
EndStructure
Procedure.s GetMimeType(Filename$)
Protected Mime$
Select LCase(GetExtensionPart(Filename$))
Case "html","htm" : Mime$ = "text/html"
Case "js" : Mime$ = "application/javascript"
Case "css" : Mime$ = "text/css"
Case "png" : Mime$ = "image/png"
Case "jpg","jpeg" : Mime$ = "image/jpeg"
Case "gif" : Mime$ = "image/gif"
Case "ico" : Mime$ = "image/vnd.microsoft.icon"
Default
Mime$ = "application/octet-stream"
EndSelect
ProcedureReturn Mime$
EndProcedure
Procedure WebServerClientThread(*Parameter.WebServerClient_Structure)
Protected.i File, Pos1, Pos2, Chunk, Res, Timeout
Protected *Buffer
Protected.q Size
Protected Received$, Header$, Type$, Parameter$, Filename$, Mime$
Debug "ClientThread " + Str(*Parameter\Client)
Received$ = PeekS(*Parameter\Buffer, *Parameter\Size, #PB_UTF8|#PB_ByteLength)
Header$ = StringField(Received$, 1, #CRLF$ + #CRLF$)
;Debug Str(*Parameter\Client) + #LF$ + Header$
Pos1 = FindString(Header$, " ")
Type$ = Left(Header$, Pos1 - 1)
Pos1 + 1
Pos2 = FindString(Header$, "HTTP", Pos1)
Pos2 - 1
Parameter$ = Mid(Header$, Pos1, Pos2 - Pos1)
Filename$ = StringField(Parameter$, 1, "?")
Parameter$ = StringField(Parameter$, 2, "?")
If Filename$ = "/"
Filename$ = "/index.html"
EndIf
Filename$ = *Parameter\RootPath$ + Filename$
Mime$ = GetMimeType(Filename$)
Size = FileSize(Filename$)
Debug Filename$ + " " + Str(Size) + " " + Str(*Parameter\Client)
If Size > 0
Header$ = "HTTP/1.1 200 OK" + #CRLF$
Header$ + "Content-Type: " + Mime$ + #CRLF$
Header$ + "Content-Length: " + Str(Size) + #CRLF$
Header$ + "Connection: close" + #CRLF$
Header$ + #CRLF$
;Debug Str(*Parameter\Client) + #LF$ + Header$
Timeout = 1000
Repeat
If Not *Parameter\Disconnected
Res = SendNetworkString(*Parameter\Client, Header$, #PB_UTF8)
If Res < StringByteLength(Header$, #PB_UTF8)
Debug "SendNetworkString failed " + Str(*Parameter\Client)
EndIf
EndIf
Timeout - 1
Until Res > 0 Or *Parameter\Disconnected Or Timeout = 0
If Not *Parameter\Disconnected And Timeout > 0
*Buffer = AllocateMemory(16348, #PB_Memory_NoClear)
If *Buffer
File = ReadFile(#PB_Any, Filename$, #PB_File_SharedRead)
If File
While Not Eof(File) And Not *Parameter\Disconnected
Chunk = ReadData(File, *Buffer, MemorySize(*Buffer))
If Chunk > 0
Timeout = 1000
Repeat
Res = SendNetworkData(*Parameter\Client, *Buffer, Chunk)
If Res < Chunk
Debug "SendNetworkData failed " + Str(Res) + " " + Str(*Parameter\Client)
EndIf
Timeout - 1
If Timeout = 0
Debug "Timeout"
Break 2
EndIf
Until Res = Chunk Or *Parameter\Disconnected
EndIf
Wend
CloseFile(File)
Else
Debug "File not open " + Str(*Parameter\Client)
EndIf
FreeMemory(*Buffer)
Else
Debug "No file buffer " + Str(*Parameter\Client)
EndIf
EndIf
Else
Header$ = "HTTP/1.1 404 Not Found" + #CRLF$
Header$ + "Content-Length: 13" + #CRLF$
Header$ + "Connection: close" + #CRLF$
Header$ + #CRLF$
Header$ + "404 Not Found"
If Not *Parameter\Disconnected
SendNetworkString(*Parameter\Client, Header$, #PB_UTF8)
EndIf
EndIf
*Parameter\Thread = 0
EndProcedure
Procedure WebServerThread(*Parameter.WebServer_Structure)
Protected.i NetEvent, Client, Offset, Size
Protected Client$
If *Parameter\ServerFlags & (#PB_Network_TLSv1 | #PB_Network_TLSv1_0 | #PB_Network_TLSv1_1 | #PB_Network_TLSv1_2 | #PB_Network_TLSv1_3)
If *Parameter\ServerFlags & #PB_Network_IPv6
Debug "HTTPS IPv6 on port " + Str(*Parameter\Port)
Else
Debug "HTTPS IPv4 on port " + Str(*Parameter\Port)
EndIf
If *Parameter\CACertificate$ <> ""
UseNetworkTLS(*Parameter\PrivateKey$, *Parameter\Certificate$, *Parameter\CACertificate$)
Else
UseNetworkTLS(*Parameter\PrivateKey$, *Parameter\Certificate$)
EndIf
Else
If *Parameter\ServerFlags & #PB_Network_IPv6
Debug "HTTP IPv6 on port " + Str(*Parameter\Port)
Else
Debug "HTTP IPv4 on port " + Str(*Parameter\Port)
EndIf
EndIf
*Parameter\Server = CreateNetworkServer(#PB_Any, *Parameter\Port, *Parameter\ServerFlags)
If *Parameter\Server
*Parameter\RootPath$ = RTrim(*Parameter\RootPath$, "/")
Repeat
Select NetworkServerEvent(*Parameter\Server)
Case #PB_NetworkEvent_Connect
Client = EventClient()
Client$ = Str(Client)
Debug "Connect " + Client$
If Not FindMapElement(*Parameter\ClientMap(), Client$)
AddMapElement(*Parameter\ClientMap(), Client$)
*Parameter\ClientMap()\Buffer = AllocateMemory(4096, #PB_Memory_NoClear)
*Parameter\ClientMap()\Client = Client
*Parameter\ClientMap()\RootPath$ = *Parameter\RootPath$
EndIf
*Parameter\ClientMap()\Disconnected = #False
*Parameter\ClientMap()\LastSeen = Date()
If Not *Parameter\ClientMap()\Buffer
Debug "Memory allocation for new client not possible"
DeleteMapElement(*Parameter\ClientMap())
EndIf
Case #PB_NetworkEvent_Data
Client = EventClient()
Client$ = Str(Client)
Debug "Data " + Client$
If FindMapElement(*Parameter\ClientMap(), Client$)
*Parameter\ClientMap()\LastSeen = Date()
Offset = 0
Repeat
Size = ReceiveNetworkData(Client, *Parameter\ClientMap()\Buffer + Offset, MemorySize(*Parameter\ClientMap()\Buffer))
If Size < 0
If Size = -1
DeleteMapElement(*Parameter\ClientMap())
EndIf
Break
Else
If Size = MemorySize(*Parameter\ClientMap()\Buffer)
Offset = MemorySize(*Parameter\ClientMap()\Buffer)
ReAllocateMemory(*Parameter\ClientMap()\Buffer, Offset + 4096, #PB_Memory_NoClear)
Size = 0
Else
Size = Offset + Size
If Size > 0
*Parameter\ClientMap()\Size = Size
*Parameter\ClientMap()\Thread = CreateThread(@WebserverClientThread(), @*Parameter\ClientMap())
Else
Break
EndIf
EndIf
EndIf
Until Size > 0
EndIf
Case #PB_NetworkEvent_Disconnect
Client = EventClient()
Client$ = Str(Client)
If FindMapElement(*Parameter\ClientMap(), Client$)
Debug "Delete " + MapKey(*Parameter\ClientMap()) + " by disconnect"
*Parameter\ClientMap()\Disconnected = #True
EndIf
Case #PB_NetworkEvent_None
Delay(10) ; Ne pas saturer le CPU / Don't stole the whole CPU !
ForEach *Parameter\ClientMap()
If *Parameter\ClientMap()\LastSeen + 60 < Date()
If Not *Parameter\ClientMap()\Disconnected
Debug "Delete " + MapKey(*Parameter\ClientMap()) + " by timeout"
EndIf
FreeMemory(*Parameter\ClientMap()\Buffer)
DeleteMapElement(*Parameter\ClientMap())
EndIf
Next
EndSelect
Until *Parameter\Exit
While MapSize(*Parameter\ClientMap())
ForEach *Parameter\ClientMap()
*Parameter\ClientMap()\Disconnected = #True
If *Parameter\ClientMap()\Thread = 0
FreeMemory(*Parameter\ClientMap()\Buffer)
DeleteMapElement(*Parameter\ClientMap())
EndIf
Next
Wend
CloseNetworkServer(*Parameter\Server)
*Parameter\Server = 0
Else
Debug "Was not able to start server " + Str(*Parameter\Port)
EndIf
EndProcedure
;-Main
Define.i Event, Exit
Define NewList WebServerList.WebServer_Structure()
AddElement(WebServerList())
WebServerList()\Port = #HTTP_Port
WebServerList()\RootPath$ = GetPathPart(ProgramFilename()) + #WWW
WebServerList()\ServerFlags = #PB_Network_TCP|#PB_Network_IPv4|#PB_Network_NoTLS
AddElement(WebServerList())
WebServerList()\Port = #HTTP_Port
WebServerList()\RootPath$ = GetPathPart(ProgramFilename()) + #WWW
WebServerList()\ServerFlags = #PB_Network_TCP|#PB_Network_IPv6|#PB_Network_NoTLS
AddElement(WebServerList())
WebServerList()\Port = #HTTPS_Port
WebServerList()\RootPath$ = GetPathPart(ProgramFilename()) + #WWW
WebServerList()\ServerFlags = #PB_Network_TCP|#PB_Network_IPv4|#PB_Network_TLSv1
WebServerList()\PrivateKey$ = "-----BEGIN PRIVATE KEY-----" + #LF$ +
"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAPEbSQq/uwESZduPtDd83qXXkSPf6lUNa17xhM2fOZQxGr0Fdmvw6IsC+QGX25EE1TG6TFQkHlM2rW8y6a3WEC/WzCNWaTCPYD/rguiAFG+4eQmwHjiJFVec0InjjSG9SX8xwS/gQeWdQniKROO4DmMJO8N7mdUhdHODSntXdr9zAgMBAAECgYAE+VMgbaQl+YMwbF6DZogRU8kivFPRPV2hr8nVlBtT+09Z5uryfx3NAFqytbdJ3penVviMI9KcVNxvFtXLSEc9KyjzgysorAfUpwFuECCLDbOXX0HlV6rgkqJdhyV6FybcDLvgcvulHQ64QdYRhW+jPx7vXk3h0/JRFqKQJsY7QQJBAPrDLJRPbAw+Mlq1fHBWk8Z1Qn1ivPAmz+2nPAgDya/xdAlb9GbFAMzCS3upIBpxW70uLI04OuTVhwYL194I5C0CQQD2JHtHp25SkIDpBgZGicEC7yAIE/wPC0P9X85UJqXx5dPx4HbEc8lqSKMbCzkbHyvjHonSHu00QxU1W6ZALFYfAkBcPWzphSl+e2Z0XWvPutkS2FFD5A0R3YUAq1J2tEX9NTj0tGF7aB36M8ImU7jeYTJYrWJv8+4d/Ll1LOgT4XtlAkAxofOV5EYTsf28fzF+wcJAtDUyS81Uv0HLcqkpQM3PdDeDm253eJ2Rp+nzxxSRynxQBNVnoELWefxp0Pw6DnajAkBF5h7fQIbwAEPrhDzhjMXU7g9k9KzkkJN/bluLbleqkkAz1kfkGtWXJdGITZuY4K/X2yp1diWQ0utZjmOmhWsl" + #LF$ +
"-----END PRIVATE KEY-----"
WebServerList()\Certificate$ = "-----BEGIN CERTIFICATE-----" + #LF$ +
"MIICnTCCAgYCCQD0AWy2vzfcpzANBgkqhkiG9w0BAQUFADCBkjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVN0YXRlMQ0wCwYDVQQHEwRDaXR5MRUwEwYDVQQKEwxPcmdhbml6YXRpb24xHDAaBgNVBAsTE09yZ2FuaXphdGlvbmFsIFVuaXQxLzAtBgNVBAMTJkNvbW1vbiBOYW1lIChlLmcuLCB5b3VyIHNlcnZlciBkb21haW4pMB4XDTI0MTEwNjE2NTI1N1oXDTI0MTIwNjE2NTI1N1owgZIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVTdGF0ZTENMAsGA1UEBxMEQ2l0eTEVMBMGA1UEChMMT3JnYW5pemF0aW9uMRwwGgYDVQQLExNPcmdhbml6YXRpb25hbCBVbml0MS8wLQYDVQQDEyZDb21tb24gTmFtZSAoZS5nLiwgeW91ciBzZXJ2ZXIgZG9tYWluKTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA8RtJCr+7ARJl24+0N3zepdeRI9/qVQ1rXvGEzZ85lDEavQV2a/DoiwL5AZfbkQTVMbpMVCQeUzatbzLprdYQL9bMI1ZpMI9gP+uC6IAUb7h5CbAeOIkVV5zQieONIb1JfzHBL+BB5Z1CeIpE47gOYwk7w3uZ1SF0c4NKe1d2v3MCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCprm5a5bg1LqCDdtwDTnRDmVcca6HoUlvbjZLmWdLjltG1McNAATppTy/bF7vT3jXLobA1Vzs2g14POjYQhPnIbRPEnNzvAe+Se3y0YeFOwYarEyFBHKHODGIPaCnXGH8gB9fgcp2SYtLaPKvXdNL44VeYGbD4+fvUcu/zkXqTSg==" + #LF$ +
"-----END CERTIFICATE-----"
WebServerList()\CACertificate$ = ""
AddElement(WebServerList())
WebServerList()\Port = #HTTPS_Port
WebServerList()\RootPath$ = GetPathPart(ProgramFilename()) + #WWW
WebServerList()\ServerFlags = #PB_Network_TCP|#PB_Network_IPv6|#PB_Network_TLSv1
WebServerList()\PrivateKey$ = "-----BEGIN PRIVATE KEY-----" + #LF$ +
"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAPEbSQq/uwESZduPtDd83qXXkSPf6lUNa17xhM2fOZQxGr0Fdmvw6IsC+QGX25EE1TG6TFQkHlM2rW8y6a3WEC/WzCNWaTCPYD/rguiAFG+4eQmwHjiJFVec0InjjSG9SX8xwS/gQeWdQniKROO4DmMJO8N7mdUhdHODSntXdr9zAgMBAAECgYAE+VMgbaQl+YMwbF6DZogRU8kivFPRPV2hr8nVlBtT+09Z5uryfx3NAFqytbdJ3penVviMI9KcVNxvFtXLSEc9KyjzgysorAfUpwFuECCLDbOXX0HlV6rgkqJdhyV6FybcDLvgcvulHQ64QdYRhW+jPx7vXk3h0/JRFqKQJsY7QQJBAPrDLJRPbAw+Mlq1fHBWk8Z1Qn1ivPAmz+2nPAgDya/xdAlb9GbFAMzCS3upIBpxW70uLI04OuTVhwYL194I5C0CQQD2JHtHp25SkIDpBgZGicEC7yAIE/wPC0P9X85UJqXx5dPx4HbEc8lqSKMbCzkbHyvjHonSHu00QxU1W6ZALFYfAkBcPWzphSl+e2Z0XWvPutkS2FFD5A0R3YUAq1J2tEX9NTj0tGF7aB36M8ImU7jeYTJYrWJv8+4d/Ll1LOgT4XtlAkAxofOV5EYTsf28fzF+wcJAtDUyS81Uv0HLcqkpQM3PdDeDm253eJ2Rp+nzxxSRynxQBNVnoELWefxp0Pw6DnajAkBF5h7fQIbwAEPrhDzhjMXU7g9k9KzkkJN/bluLbleqkkAz1kfkGtWXJdGITZuY4K/X2yp1diWQ0utZjmOmhWsl" + #LF$ +
"-----END PRIVATE KEY-----"
WebServerList()\Certificate$ = "-----BEGIN CERTIFICATE-----" + #LF$ +
"MIICnTCCAgYCCQD0AWy2vzfcpzANBgkqhkiG9w0BAQUFADCBkjELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVN0YXRlMQ0wCwYDVQQHEwRDaXR5MRUwEwYDVQQKEwxPcmdhbml6YXRpb24xHDAaBgNVBAsTE09yZ2FuaXphdGlvbmFsIFVuaXQxLzAtBgNVBAMTJkNvbW1vbiBOYW1lIChlLmcuLCB5b3VyIHNlcnZlciBkb21haW4pMB4XDTI0MTEwNjE2NTI1N1oXDTI0MTIwNjE2NTI1N1owgZIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVTdGF0ZTENMAsGA1UEBxMEQ2l0eTEVMBMGA1UEChMMT3JnYW5pemF0aW9uMRwwGgYDVQQLExNPcmdhbml6YXRpb25hbCBVbml0MS8wLQYDVQQDEyZDb21tb24gTmFtZSAoZS5nLiwgeW91ciBzZXJ2ZXIgZG9tYWluKTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA8RtJCr+7ARJl24+0N3zepdeRI9/qVQ1rXvGEzZ85lDEavQV2a/DoiwL5AZfbkQTVMbpMVCQeUzatbzLprdYQL9bMI1ZpMI9gP+uC6IAUb7h5CbAeOIkVV5zQieONIb1JfzHBL+BB5Z1CeIpE47gOYwk7w3uZ1SF0c4NKe1d2v3MCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCprm5a5bg1LqCDdtwDTnRDmVcca6HoUlvbjZLmWdLjltG1McNAATppTy/bF7vT3jXLobA1Vzs2g14POjYQhPnIbRPEnNzvAe+Se3y0YeFOwYarEyFBHKHODGIPaCnXGH8gB9fgcp2SYtLaPKvXdNL44VeYGbD4+fvUcu/zkXqTSg==" + #LF$ +
"-----END CERTIFICATE-----"
WebServerList()\CACertificate$ = ""
ForEach WebServerList()
WebServerList()\Thread = CreateThread(@WebServerThread(), @WebServerList())
Next
If OpenWindow(0, 0, 0, 800, 600, "AppComposer", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget)
If CreateMenu(0, WindowID(0)) ; Create a regular menu with title and one item
MenuTitle("File")
MenuItem(1, "Load HTTP")
MenuItem(2, "Load HTTPS")
EndIf
WebViewGadget(0, 0, 0, 800, 600)
SetGadgetText(0, "http://localhost:" + Str(#HTTP_PORT))
HideWindow(0, #False)
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Menu
Select EventMenu()
Case 1 : SetGadgetText(0, "http://localhost:" + Str(#HTTP_PORT))
Case 2 : SetGadgetText(0, "https://localhost:" + Str(#HTTPS_PORT))
EndSelect
Case #PB_Event_CloseWindow
Debug "CloseWindow"
Exit = #True
EndSelect
Until Exit
ForEach WebServerList()
If IsThread(WebServerList()\Thread)
WebServerList()\Exit = #True
If WaitThread(WebServerList()\Thread, 3000) = 0
Debug "Should never reached"
KillThread(WebServerList()\Thread)
EndIf
EndIf
Next
EndIf
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Mon Dec 29, 2025 11:58 pm
by infratec
I changed the code above, because my stress test failed (windows):
Code: Select all
curl --parallel --parallel-immediate --parallel-max-host 1000 -o nul "http://localhost:8080/portmon.exe?[1-1000]"
But for the new parameter --parallel-max-host you need to download curl 8.16 or higher
SendNetworkData() returned -1 an the delivery stucked.
portmon.exe is 451392 bytes in size.
DL% UL% Dled Uled Xfers Live Total Current Left Speed
100 -- 430M 0 1000 0 0:00:04 0:00:05 --:--:-- 89.9M
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 30, 2025 7:02 am
by skinkairewalker
infratec wrote: Mon Dec 29, 2025 10:22 pm
You can try this:
Code: Select all
EnableExplicit
#PORT = 8080
#WWW = "www"
Structure WebServerClient_Structure
Client.i
Thread.i
*Buffer
Size.i
LastSeen.i
RootPath$
EndStructure
Structure WebServer_Structure
Thread.i
Port.i
Server.i
RootPath$
Map ClientMap.WebServerClient_Structure()
Exit.i
EndStructure
Procedure.s GetMimeType(Filename$)
Protected Mime$
Select LCase(GetExtensionPart(Filename$))
Case "html","htm" : Mime$ = "text/html"
Case "js" : Mime$ = "application/javascript"
Case "css" : Mime$ = "text/css"
Case "png" : Mime$ = "image/png"
Case "jpg","jpeg" : Mime$ = "image/jpeg"
Case "gif" : Mime$ = "image/gif"
Case "ico" : Mime$ = "image/vnd.microsoft.icon"
Default
Mime$ = "application/octet-stream"
EndSelect
ProcedureReturn Mime$
EndProcedure
Procedure WebServerClientThread(*Parameter.WebServerClient_Structure)
Protected.i File, Pos1, Pos2, Chunk, Res
Protected *Buffer
Protected.q Size
Protected Received$, Header$, Type$, Parameter$, Filename$, Mime$
Debug "ClientThread " + Str(*Parameter\Client)
Received$ = PeekS(*Parameter\Buffer, *Parameter\Size, #PB_UTF8|#PB_ByteLength)
Header$ = StringField(Received$, 1, #CRLF$ + #CRLF$)
;Debug Str(*Parameter\Client) + #LF$ + Header$
Pos1 = FindString(Header$, " ")
Type$ = Left(Header$, Pos1 - 1)
Pos1 + 1
Pos2 = FindString(Header$, "HTTP", Pos1)
Pos2 - 1
Parameter$ = Mid(Header$, Pos1, Pos2 - Pos1)
Filename$ = StringField(Parameter$, 1, "?")
Parameter$ = StringField(Parameter$, 2, "?")
If Filename$ = "/"
Filename$ = "/index.html"
EndIf
Filename$ = *Parameter\RootPath$ + Filename$
Mime$ = GetMimeType(Filename$)
Size = FileSize(Filename$)
Debug Filename$ + " " + Str(Size) + " " + Str(*Parameter\Client)
If Size > 0
Header$ = "HTTP/1.1 200 OK" + #CRLF$
Header$ + "Content-Type: " + Mime$ + #CRLF$
Header$ + "Content-Length: " + Str(Size) + #CRLF$
Header$ + "Connection: close" + #CRLF$
Header$ + #CRLF$
;Debug Str(*Parameter\Client) + #LF$ + Header$
Res = SendNetworkString(*Parameter\Client, Header$, #PB_UTF8)
If Res < StringByteLength(Header$, #PB_UTF8)
Debug "SendNetworkString failed " + Str(*Parameter\Client)
EndIf
*Buffer = AllocateMemory(16348, #PB_Memory_NoClear)
If *Buffer
File = ReadFile(#PB_Any, Filename$, #PB_File_SharedRead)
If File
While Not Eof(File)
Chunk = ReadData(File, *Buffer, MemorySize(*Buffer))
If Chunk > 0
Repeat
Res = SendNetworkData(*Parameter\Client, *Buffer, Chunk)
If Res < Chunk
Debug "SendNetworkData failed " + Str(Res) + " " + Str(*Parameter\Client)
EndIf
Until Res = Chunk
EndIf
Wend
CloseFile(File)
Else
Debug "File not open " + Str(*Parameter\Client)
EndIf
FreeMemory(*Buffer)
Else
Debug "No file buffer " + Str(*Parameter\Client)
EndIf
Else
Header$ = "HTTP/1.1 404 Not Found" + #CRLF$
Header$ + "Content-Length: 13" + #CRLF$ +
Header$ + "Connection: close" + #CRLF$ +
Header$ + #CRLF$ +
Header$ + "404 Not Found"
SendNetworkString(*Parameter\Client, Header$, #PB_UTF8)
EndIf
*Parameter\Thread = 0
EndProcedure
Procedure WebServerThread(*Parameter.WebServer_Structure)
Protected.i NetEvent, Client, Offset, Size
Protected Client$
*Parameter\Server = CreateNetworkServer(#PB_Any, *Parameter\Port, #PB_Network_TCP|#PB_Network_IPv4)
If *Parameter\Server
Debug "Servidor em http://localhost:" + Str(#PORT)
Repeat
Select NetworkServerEvent()
Case #PB_NetworkEvent_Connect
Client = EventClient()
Client$ = Str(Client)
Debug "Connect " + Client$
If Not FindMapElement(*Parameter\ClientMap(), Client$)
AddMapElement(*Parameter\ClientMap(), Client$)
*Parameter\ClientMap()\Buffer = AllocateMemory(4096, #PB_Memory_NoClear)
*Parameter\ClientMap()\Client = Client
*Parameter\ClientMap()\RootPath$ = *Parameter\RootPath$
EndIf
*Parameter\ClientMap()\LastSeen = Date()
If Not *Parameter\ClientMap()\Buffer
Debug "Memory allocation for new client not possible"
DeleteMapElement(*Parameter\ClientMap())
EndIf
Case #PB_NetworkEvent_Data
Client = EventClient()
Client$ = Str(Client)
Debug "Data " + Client$
If FindMapElement(*Parameter\ClientMap(), Client$)
*Parameter\ClientMap()\LastSeen = Date()
Offset = 0
Repeat
Size = ReceiveNetworkData(Client, *Parameter\ClientMap()\Buffer + Offset, MemorySize(*Parameter\ClientMap()\Buffer))
If Size = -1
DeleteMapElement(*Parameter\ClientMap())
Break
Else
If Size = MemorySize(*Parameter\ClientMap()\Buffer)
Offset = MemorySize(*Parameter\ClientMap()\Buffer)
ReAllocateMemory(*Parameter\ClientMap()\Buffer, Offset + 4096, #PB_Memory_NoClear)
Size = 0
Else
Size = Offset + Size
*Parameter\ClientMap()\Size = Size
*Parameter\ClientMap()\Thread = CreateThread(@WebserverClientThread(), @*Parameter\ClientMap())
EndIf
EndIf
Until Size > 0
EndIf
Case #PB_NetworkEvent_Disconnect
Client = EventClient()
Client$ = Str(Client)
If FindMapElement(*Parameter\ClientMap(), Client$)
Debug "Delete " + MapKey(*Parameter\ClientMap()) + " by disconnect"
FreeMemory(*Parameter\ClientMap()\Buffer)
DeleteMapElement(*Parameter\ClientMap())
EndIf
Case #PB_NetworkEvent_None
Delay(10) ; Ne pas saturer le CPU / Don't stole the whole CPU !
ForEach *Parameter\ClientMap()
If *Parameter\ClientMap()\LastSeen + 60 < Date()
Debug "Delete " + MapKey(*Parameter\ClientMap()) + " by timeout"
FreeMemory(*Parameter\ClientMap()\Buffer)
DeleteMapElement(*Parameter\ClientMap())
EndIf
Next
EndSelect
Until *Parameter\Exit
While MapSize(*Parameter\ClientMap())
ForEach *Parameter\ClientMap()
If *Parameter\ClientMap()\Thread = 0
FreeMemory(*Parameter\ClientMap()\Buffer)
DeleteMapElement(*Parameter\ClientMap())
EndIf
Next
Wend
CloseNetworkServer(*Parameter\Server)
*Parameter\Server = 0
EndIf
EndProcedure
;-Main
Define.i Event, Exit
Define WebServer.WebServer_Structure
WebServer\Port = #Port
WebServer\RootPath$ = GetPathPart(ProgramFilename()) + #WWW
WebServer\Thread = CreateThread(@WebServerThread(), @WebServer)
If OpenWindow(0, 0, 0, 800, 600, "AppComposer", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget)
If CreateMenu(0, WindowID(0)) ; Create a regular menu with title and one item
MenuTitle("File")
MenuItem(1, "Reload" + #TAB$ + "Ctrl+O")
EndIf
WebViewGadget(0, 0, 0, 800, 600)
SetGadgetText(0, "http://localhost:8080")
HideWindow(0, #False)
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Menu
Select EventMenu()
Case 1
SetGadgetText(0, "http://localhost:8080")
EndSelect
Case #PB_Event_CloseWindow
Exit = #True
EndSelect
Until Exit
If IsThread(WebServer\Thread)
WebServer\Exit = #True
If WaitThread(WebServer\Thread, 3000) = 0
Debug "Should never reached"
KillThread(WebServer\Thread)
EndIf
EndIf
EndIf
I thank you infinitely for the help, thank you for sharing your knowledge with me!
I will test it now, thank you so much. I’ll be back soon with the results I get, thank you for your time.
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 30, 2025 10:24 am
by infratec
Btw. If you want a TCPv6 Server you need to start an extra server.
And if you want http/https and IPv4/v6 you need 4 server threads running.
The parameters for the server thread can be extended to reflect this.
Then you can start the 'same' server for the different conditions.
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 30, 2025 11:42 am
by infratec
Added the possibility for https.
Some more tricks were needed, mainly to fix -2 return value for ReceiveNetworkData() with TLS
The speed with TLS is ... reduced:
curl -k --parallel --parallel-immediate --parallel-max-host 1000 -o nul "
https://localhost:8443/portmon.exe?[1-1000]"
DL% UL% Dled Uled Xfers Live Total Current Left Speed
100 -- 430M 0 1000 0 0:00:12 0:00:12 --:--:-- 35.8M
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Tue Dec 30, 2025 5:29 pm
by infratec
Since I had a bit time, I added the possibility for all variants.
Pittfall: You need to use the NetworkServerEvent() parameter
Else the events get mixed between the servers.
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Wed Dec 31, 2025 1:47 pm
by infratec
I think the most problem is your stresstest code.
It does not receive the data and at the end it is terminated without closing all correct.
The problem on the server side is, that ther eis no way in PB to detect if a network connection is still valid or not.
So the server tries to send data to a no longer existant connection and run into trouble.
Re: Simple HTTP Server crashes on linux/mac on stress test
Posted: Wed Dec 31, 2025 2:58 pm
by infratec
I tested it on linux and added some stuff to avoid endless loops and segmentation faults cause of not correct connections.
The server works in my environment.