diff --git a/lweb.pbi b/lweb.pbi index e3f88dd..7a5270b 100644 --- a/lweb.pbi +++ b/lweb.pbi @@ -80,6 +80,7 @@ Module lhs_web Structure s_client_memory Buffer.i Initialized.b + Size.i EndStructure Structure s_file_cache @@ -100,6 +101,19 @@ Module lhs_web client_test_srv.i EndStructure + Structure s_request_handler + call.i + type.i + routetype.i + EndStructure + + + + Enumeration cli_handler_infos 1 + #get_handler_procedure ;Funktion die Aufgerufen werden muss + #get_handler_prototype ;Welcher Prototype + EndEnumeration + Global.i server_id Global.i server_mutex = CreateMutex() ;Dieser Mutex dient zu der Sicherheit der Element Liste. Global.i file_cache_mutex = CreateMutex() @@ -108,9 +122,19 @@ Module lhs_web Global NewMap m_file_cache_map.i() Global NewMap m_file_cache.s_file_cache() Global NewMap m_clients.s_clients(conf_max_clients) + Global NewMap m_request.s_request_handler() ;} + ;******************************** + ;* + ;* Handler Prototypen + ;* + + Prototype.s WebHandler_Get(Map handler_Map.s()) + Prototype.s WebHandler_Post(Map handler_Map.s(), ContentString.s) + Prototype.s WebHandler_Universal(Map handler_Map.s(), ContentString.s) + ;******************************** ;* ;* Proceduren Deklarierung @@ -120,12 +144,12 @@ Module lhs_web Declare client(network_client_id.i) Declare AddFileToCache(MemoryID.i, FileName.s, Size.i) Declare FileCache(Information.i) - - Declare.s mimetype(file.s) + Declare call_request(RequestString.s, Info.i=#get_handler_procedure) Declare.s http_day(Tag.i) Declare.s http_month(Monat.i) Declare.s http_header_generate(Map Header.s()) Declare.s Work_Header_to_JSONMap(String.s) + Declare.s Work_Post_ToJSON_x_www_form_urlencoded(ContentLength.i, MemorSize.i, Memory.i) Declare.s GetFileFromCache(FileName.s, MemoryID.i) Procedure set_config(parameter.i=#conf_defaultfile, setting.s="index.html") @@ -174,6 +198,8 @@ Module lhs_web ProcedureReturn Str(conf_max_clients) Case #conf_server_type ProcedureReturn Str(conf_server_type) + Case #conf_port + ProcedureReturn Str(conf_port) Default ProcedureReturn "" EndSelect @@ -208,7 +234,7 @@ Module lhs_web thread_alive = #True Debug "AllOk" Repeat - ;Daten Sendeauftrag vorhanden? + ;Ist etwas passiert ? network_event = NetworkServerEvent(network_server_id) Select network_event @@ -311,15 +337,17 @@ Module lhs_web Procedure client(network_client_id.i) Protected thread_cli_id = network_client_id - Protected cli_stop, MyThreadJSON - Protected thread_temp_string.s, thread_temp_cache.s, thread_temp_cache_memory, temp_receivelength + Protected cli_stop, MyThreadJSON, ToCall, ToCallType + Protected thread_temp_string.s, thread_temp_cache.s, thread_temp_cache_memory, temp_receivelength, thread_temp_decode_memory Protected thread_command_length, thread_max_pos, thread_pos, thread_reasign - Protected thread_file_size, thread_file_handle - Protected.s thread_requested, thread_type, thread_date, thread_header, thread_work, JSONStringToMap + Protected thread_data_size, thread_file_handle + Protected.s thread_requested, thread_type, thread_date, thread_header, thread_work, JSONStringToMap, Handler_Response, response_status, PostMapString Protected thread_buffer, thread_buffer_offset, thread_buffer_length, buffer_sent Protected.b thread_alive = #True, error_message = #False Define NewMap Header.s() - ;{ + Define NewMap Response.s() + Define NewMap Post.s() + Debug "Client Thread Started. ID:" + Str(network_client_id) Repeat ;Prüfen ob der Thread was zu tun hat. @@ -340,8 +368,10 @@ Module lhs_web EndIf temp_receivelength = ReceiveNetworkData(thread_cli_id, m_clients(Str(thread_cli_id))\datenbuffer()\Buffer, 65536) If temp_receivelength = -1 + Debug "Empfangsfehler." Break ElseIf temp_receivelength = 65536 + m_clients(Str(thread_cli_id))\datenbuffer()\Size = temp_receivelength counter_mem_buffers = 2 Repeat AddElement(m_clients(Str(thread_cli_id))\datenbuffer()) @@ -353,11 +383,14 @@ Module lhs_web Break 2 EndIf temp_receivelength = ReceiveNetworkData(thread_cli_id, m_clients(Str(thread_cli_id))\datenbuffer()\Buffer, 65536) + m_clients(Str(thread_cli_id))\datenbuffer()\Size = temp_receivelength counter_mem_buffers + 1 If temp_receivelength = -1 Break 2 EndIf Until temp_receivelength < 65536 + Else + m_clients(Str(thread_cli_id))\datenbuffer()\Size = temp_receivelength EndIf ;Alle Daten empfangen. Debug "Anzahl Buffer:" + Str(ListSize(m_clients(Str(thread_cli_id))\datenbuffer())) @@ -382,16 +415,19 @@ Module lhs_web Case #CLI_DO_DataWorking ;Empfang abgeschlossen ;Buffer Bearbeiten - ;Thread in Bearbeitungsmodus Setzen. + ;Thread in Bearbeitungsmodus Setzen. + ;TODO: Was ist mit zusammenhängenden Datenbuffern grosse Post/Put z.B. + ;{ Debug "Daten vollständig empfangen. Abarbeiten." m_clients(Str(thread_cli_id))\client_do_cli = #CLI_DO_DataWorking ;Abarbeiten der Anforderung... ResetList(m_clients(Str(thread_cli_id))\datenbuffer()) While NextElement(m_clients(Str(thread_cli_id))\datenbuffer()) - - thread_work = PeekS(m_clients(Str(thread_cli_id))\datenbuffer()\Buffer, 16384, #PB_Ascii) ;Header solte wohl nicht grösser als 16KB sein + + thread_work = PeekS(m_clients(Str(thread_cli_id))\datenbuffer()\Buffer, m_clients(Str(thread_cli_id))\datenbuffer()\Size, #PB_Ascii) ;Header to Map + Debug "Datenbuffer:"+ Mid(thread_work,1,256) JSONStringToMap = Work_Header_to_JSONMap(thread_work) If JSONStringToMap <> #error_string MyThreadJSON = ParseJSON(#PB_Any, JSONStringToMap) @@ -401,208 +437,360 @@ Module lhs_web FreeJSON(MyThreadJSON) Else ;WTF ??? - Break ; Thread abschiessen + Break 2 ; Thread abschiessen EndIf Else error_message = #True EndIf - Debug Mid(thread_work,1,256) - Debug JSONStringToMap - If Header(#http_head_method) = #http_method_get - thread_command_length = 3 - ;******************************** - ;* - ;* Default GET - ;* - - Debug #http_method_get - If Header(#http_head_request) = "/" - thread_requested = conf_defaultfile - Else - thread_requested = Header(#http_head_request) - EndIf - If conf_cache_enable = 1 - thread_temp_cache_memory = AllocateMemory(1024) - thread_temp_cache = GetFileFromCache(thread_requested, thread_temp_cache_memory) - If thread_temp_cache = #error_string - thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) + Debug "JSONString:"+ JSONStringToMap + thread_type = "" + Select Header(#http_head_method) + Case #http_method_get + ;******************************** + ;* + ;* Default GET + ;* + ;{ + Debug #http_method_get + If Header(#http_head_request) = "/" + thread_requested = conf_defaultfile + Else + thread_requested = Header(#http_head_request) + EndIf + Debug "Requested:"+thread_requested + ToCallType = call_request(thread_requested, #get_handler_prototype) + If ToCallType = #handler_proto_universal Or ToCallType = #handler_proto_get + ToCall = call_request(thread_requested) + Else + ToCall = 0 + EndIf - If thread_file_handle - ;Alles Ok + If ToCall > 0 ;Dann ist eine Funktion hinterlegt und zulässig aufgerufen zu werden. + ;{ Dynamischer WebHandler + + Select ToCallType + Case #handler_proto_universal + Define.WebHandler_Universal ToCallProcedure = ToCall + Handler_Response = ToCallProcedure(Header(), "") + + Case #handler_proto_get + Define.WebHandler_Get ToCallProcedure = ToCall + Handler_Response = ToCallProcedure(Header()) + + EndSelect + Debug "Main Client Response :"+Handler_Response + MyThreadJSON = ParseJSON(#PB_Any, Handler_Response) + If MyThreadJSON + ClearMap(Response()) + ExtractJSONMap(JSONValue(MyThreadJSON), Response()) + FreeJSON(MyThreadJSON) Else - thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) - Debug conf_basedir + conf_defaultfile - If Not thread_file_handle - thread_file_handle = ReadFile(#PB_Any, "error.html") - EndIf - + ;WTF ??? + Debug "Fehler Absturz" + Break 2 ; Thread abschiessen EndIf - thread_file_size = Lof(thread_file_handle) - thread_temp_cache_memory = ReAllocateMemory(thread_temp_cache_memory, thread_file_size) - ReadData(thread_file_handle, thread_temp_cache_memory, thread_file_size) - CloseFile(thread_file_handle) - AddFileToCache(thread_temp_cache_memory, thread_requested, thread_file_size) + Debug "Response Content:"+Response(#cha_R_ResponseContentType) + Select Response(#cha_R_ResponseContentType) + Case #response_Memory + thread_data_size = Val(Response(#cha_R_MemorySize)) + thread_temp_cache_memory = Val(Response(#cha_R_MemoryAdress)) + thread_type = Response(#cha_R_ResponseType) + Case #response_string + thread_temp_decode_memory = AllocateMemory(StringByteLength(Response(#cha_R_StringBase64))) + thread_data_size = Base64Decoder(Response(#cha_R_StringBase64), thread_temp_decode_memory, StringByteLength(Response(#cha_R_StringBase64))) + thread_temp_cache_memory = AllocateMemory(thread_data_size) + CopyMemory(thread_temp_decode_memory, thread_temp_cache_memory, thread_data_size) + FreeMemory(thread_temp_decode_memory) + thread_type = Response(#cha_R_ResponseType) + Default + ;Solte ja nicht passieren. + EndSelect + Debug "Content Finished" + ;} + + ElseIf conf_cache_enable = 1 + ;{ Cached File Handling BUGGY!!!!!!! + thread_temp_cache_memory = AllocateMemory(1024) + thread_temp_cache = GetFileFromCache(thread_requested, thread_temp_cache_memory) + If thread_temp_cache = #error_string + thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) + + If thread_file_handle + ;Alles Ok + Else + thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) + Debug "FileDir:" + conf_basedir + conf_defaultfile + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, "error.html") + EndIf + + EndIf + thread_data_size = Lof(thread_file_handle) + thread_temp_cache_memory = ReAllocateMemory(thread_temp_cache_memory, thread_data_size) + ReadData(thread_file_handle, thread_temp_cache_memory, thread_data_size) + CloseFile(thread_file_handle) + AddFileToCache(thread_temp_cache_memory, thread_requested, thread_data_size) + + Else + thread_data_size = Val(StringField(thread_temp_cache, 1, ":")) + thread_temp_cache_memory = Val(StringField(thread_temp_cache, 2, ":")) + EndIf + ;} Else - thread_file_size = Val(StringField(thread_temp_cache, 1, ":")) - thread_temp_cache_memory = Val(StringField(thread_temp_cache, 2, ":")) - EndIf - Else - thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) - If Not thread_file_handle - thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) - Debug conf_basedir + conf_defaultfile - If Not thread_file_handle - thread_file_handle = ReadFile(#PB_Any, "error.html") - EndIf + ;{ Uncached File Handling scheint stabil zu funktionieren mit Getestet 200 Clients à 100 Requests + thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) + Debug "FileDir:" + conf_basedir + conf_defaultfile + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, "error.html") + EndIf + EndIf + + thread_data_size = Lof(thread_file_handle) + thread_temp_cache_memory = AllocateMemory(thread_data_size) + ReadData(thread_file_handle, thread_temp_cache_memory, thread_data_size) + CloseFile(thread_file_handle) + ;} + EndIf - thread_file_size = Lof(thread_file_handle) - thread_temp_cache_memory = AllocateMemory(thread_file_size) - ReadData(thread_file_handle, thread_temp_cache_memory, thread_file_size) - CloseFile(thread_file_handle) - EndIf - - - If thread_file_size - thread_type = mimetype(GetExtensionPart(thread_requested)) - thread_date = http_day(DayOfWeek(Date())) + - Str(Day(Date())) + - http_month(Month(Date())) + - Str(Year(Date())) + - " " + - FormatDate("%hh:%ii:%ss GMT+1", Date()) - - ;lweb_srv_mod_mss() - ClearMap(Header()) - Header(#http_head_status) = "200 OK" - Header(#http_head_content_length) = Str(thread_file_size) - Header(#http_head_content_type) = thread_type - Header(#http_head_connection) = "Keep-Alive" - Header(#http_head_keep_alive) = "timeout=15, max=1000" - thread_header = http_header_generate(Header()) - thread_buffer = AllocateMemory(thread_file_size+StringByteLength(thread_header)+12) - thread_buffer_offset = thread_buffer - thread_buffer_length = PokeS(thread_buffer_offset, thread_header,-1, #PB_UTF8|#PB_String_NoZero) : thread_buffer_offset + thread_buffer_length - Debug "Header Finished" - EndIf - - - ElseIf Header(#http_head_method) = #http_method_post - ;******************************** - ;* - ;* POST - ;* - Debug #http_method_post - ;PrintN("POST") - - If Header(#http_head_request) = "/" - thread_requested = conf_defaultfile - Else - thread_requested = Header(#http_head_request) - EndIf - - thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) - - If Not thread_file_handle - thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) - Debug conf_basedir + thread_requested - If Not thread_file_handle - thread_file_handle = ReadFile(#PB_Any, "error.html") - EndIf - - EndIf - If thread_file_handle - thread_file_size = Lof(thread_file_handle) - thread_type = mimetype(GetExtensionPart(thread_requested)) - thread_date = http_day(DayOfWeek(Date())) + - Str(Day(Date())) + - http_month(Month(Date())) + - Str(Year(Date())) + - " " + - FormatDate("%hh:%ii:%ss GMT+1", Date()) - - thread_buffer = AllocateMemory(thread_file_size+500) - thread_buffer_offset = thread_buffer - - - ;HEADER and Strings: - ; POST /login.mss HTTP/1.1 - ; Host: localhost:61000 - ; User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0 - ; Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 - ; Accept-Language: de,en-US;q=0.7,en;q=0.3 - ; Accept-Encoding: gzip, deflate - ; Referer: http://localhost:61000/index.html - ; Connection: keep-alive - ; Content-Type: application/x-www-form-urlencoded - ; Content-Length: 27 - ; - ; username=test&passwort=test - - ;********************************3 - ;* Script öffnen - ;* - ;* Akzeptierte Parameter: - ;* - ;* username - ;* passwort - ;* sid - ;* - ;* Identifikation und übergabe des paramters an script ... - ;Fixe Loginfunktion mit user und pasd - ; Position_String = FindString(Temp_String, "Content-Length:") - ; - ; If Position_String > 0 - ; ;Länge Content identifizieren. - ; - ; - ; EndIf - ; - ; While NextElement(HTTP_List()) - ; If HTTP_List()\ID = Event_Network_Client - ; HTTP_List()\Login = 1 - ; Break - ; Else - ; ;Do Nothing - ; - ; EndIf - ; Wend - + If thread_data_size + If thread_type ="" + thread_type = mimetype(GetExtensionPart(thread_requested)) + EndIf + + thread_date = http_day(DayOfWeek(Date())) + + Str(Day(Date())) + + http_month(Month(Date())) + + Str(Year(Date())) + + " " + + FormatDate("%hh:%ii:%ss GMT+1", Date()) + + ;lweb_srv_mod_mss() - ClearMap(Header()) - Header(#http_head_status) = "200 OK" - Header(#http_head_content_length) = Str(thread_file_size) - Header(#http_head_content_type) = thread_type - thread_header = http_header_generate(Header()) - thread_buffer_length = PokeS(thread_buffer_offset, thread_header,-1, #PB_UTF8|#PB_String_NoZero) : thread_buffer_offset + thread_buffer_length - Debug "Header Finished" - EndIf - Else - ;************************************ - ;* - ;* Not a supported Command in HTTP Header Cleanup. - ;* - ;* Read Buffer to Memory and Clear Buffer to Zero until the complete Networkbuffer from this Client is Cleaned. - ;* - - EndIf + ClearMap(Header()) + If Response(#cha_R_http_head_status) <> "" + Header(#http_head_status) = Response(#cha_R_http_head_status) + ElseIf response_status <> "" + Header(#http_head_status) = response_status + Else + Header(#http_head_status) = "200 OK" + EndIf + + Header(#http_head_content_length) = Str(thread_data_size) + Header(#http_head_content_type) = thread_type + Header(#http_head_connection) = "Keep-Alive" + Header(#http_head_keep_alive) = "timeout=15, max=1000" + thread_header = http_header_generate(Header()) + thread_buffer = AllocateMemory(thread_data_size+StringByteLength(thread_header)+12) + thread_buffer_offset = thread_buffer + thread_buffer_length = PokeS(thread_buffer_offset, thread_header,-1, #PB_UTF8|#PB_String_NoZero) : thread_buffer_offset + thread_buffer_length + Debug "Header Finished" + EndIf + ;} + Case #http_method_post + + ;******************************** + ;* + ;* POST + ;* + Debug #http_method_post + ;PrintN("POST") + If LCase(Header(#http_head_content_type)) = #http_content_type_application_x_www_form_urlencoded + PostMapString = Work_Post_ToJSON_x_www_form_urlencoded(Val(Header(#http_head_content_length)), m_clients(Str(thread_cli_id))\datenbuffer()\Size, m_clients(Str(thread_cli_id))\datenbuffer()\Buffer) + MyThreadJSON = ParseJSON(#PB_Any, PostMapString) + If MyThreadJSON + ClearMap(Post()) + ExtractJSONMap(JSONValue(MyThreadJSON), Post()) + FreeJSON(MyThreadJSON) + Else + ;WTF ??? + Debug "Fehler Absturz" + Break 2 ; Thread abschiessen + EndIf + EndIf + + If Header(#http_head_request) = "/" + thread_requested = conf_defaultfile + Else + thread_requested = Header(#http_head_request) + EndIf + Debug "Requested:"+thread_requested + ToCallType = call_request(thread_requested, #get_handler_prototype) + If ToCallType = #handler_proto_universal Or ToCallType = #handler_proto_post + ToCall = call_request(thread_requested) + Else + ToCall = 0 + EndIf + + If ToCall > 0 ;Dann ist eine Funktion hinterlegt und zulässig aufgerufen zu werden. + ;{ Dynamischer WebHandler + + Select ToCallType + Case #handler_proto_universal + Define.WebHandler_Universal ToCallProcedure = ToCall + Handler_Response = ToCallProcedure(Header(), PostMapString ) + + Case #handler_proto_post + Define.WebHandler_Post ToCallProcedure = ToCall + Handler_Response = ToCallProcedure(Header(), PostMapString) + + EndSelect + Debug "Main Client Response :"+Handler_Response + MyThreadJSON = ParseJSON(#PB_Any, Handler_Response) + If MyThreadJSON + ClearMap(Response()) + ExtractJSONMap(JSONValue(MyThreadJSON), Response()) + FreeJSON(MyThreadJSON) + Else + ;WTF ??? + Debug "Fehler Absturz" + Break 2 ; Thread abschiessen + EndIf + Debug "Response Content:"+Response(#cha_R_ResponseContentType) + Select Response(#cha_R_ResponseContentType) + Case #response_Memory + thread_data_size = Val(Response(#cha_R_MemorySize)) + thread_temp_cache_memory = Val(Response(#cha_R_MemoryAdress)) + thread_type = Response(#cha_R_ResponseType) + Case #response_string + thread_temp_decode_memory = AllocateMemory(StringByteLength(Response(#cha_R_StringBase64))) + thread_data_size = Base64Decoder(Response(#cha_R_StringBase64), thread_temp_decode_memory, StringByteLength(Response(#cha_R_StringBase64))) + thread_temp_cache_memory = AllocateMemory(thread_data_size) + CopyMemory(thread_temp_decode_memory, thread_temp_cache_memory, thread_data_size) + FreeMemory(thread_temp_decode_memory) + thread_type = Response(#cha_R_ResponseType) + Default + ;Solte ja nicht passieren. + EndSelect + Debug "Content Finished" + ;} + + ElseIf conf_cache_enable = 1 + ;{ Cached File Handling BUGGY!!!!!!! + thread_temp_cache_memory = AllocateMemory(1024) + thread_temp_cache = GetFileFromCache(thread_requested, thread_temp_cache_memory) + If thread_temp_cache = #error_string + thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) + + If thread_file_handle + ;Alles Ok + Else + thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) + Debug "FileDir:" + conf_basedir + conf_defaultfile + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, "error.html") + EndIf + + EndIf + thread_data_size = Lof(thread_file_handle) + thread_temp_cache_memory = ReAllocateMemory(thread_temp_cache_memory, thread_data_size) + ReadData(thread_file_handle, thread_temp_cache_memory, thread_data_size) + CloseFile(thread_file_handle) + AddFileToCache(thread_temp_cache_memory, thread_requested, thread_data_size) + + Else + thread_data_size = Val(StringField(thread_temp_cache, 1, ":")) + thread_temp_cache_memory = Val(StringField(thread_temp_cache, 2, ":")) + EndIf + ;} + + Else + ;{ Uncached File Handling scheint stabil zu funktionieren mit Getestet 200 Clients à 100 Requests + thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) + Debug "FileDir:" + conf_basedir + conf_defaultfile + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, "error.html") + EndIf + EndIf + + thread_data_size = Lof(thread_file_handle) + thread_temp_cache_memory = AllocateMemory(thread_data_size) + ReadData(thread_file_handle, thread_temp_cache_memory, thread_data_size) + CloseFile(thread_file_handle) + ;} + + EndIf + If Header(#http_head_request) = "/" + thread_requested = conf_defaultfile + Else + thread_requested = Header(#http_head_request) + EndIf + + thread_file_handle = ReadFile(#PB_Any, conf_basedir + thread_requested,#PB_File_SharedRead) + + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, conf_basedir + conf_defaultfile,#PB_File_SharedRead) + Debug "FileDir:" + conf_basedir + thread_requested + If Not thread_file_handle + thread_file_handle = ReadFile(#PB_Any, "error.html") + EndIf + + EndIf + If thread_data_size + If thread_type ="" + thread_type = mimetype(GetExtensionPart(thread_requested)) + EndIf + + thread_date = http_day(DayOfWeek(Date())) + + Str(Day(Date())) + + http_month(Month(Date())) + + Str(Year(Date())) + + " " + + FormatDate("%hh:%ii:%ss GMT+1", Date()) + + + ;lweb_srv_mod_mss() + ClearMap(Header()) + If Response(#cha_R_http_head_status) <> "" + Header(#http_head_status) = Response(#cha_R_http_head_status) + ElseIf response_status <> "" + Header(#http_head_status) = response_status + Else + Header(#http_head_status) = "200 OK" + EndIf + + Header(#http_head_content_length) = Str(thread_data_size) + Header(#http_head_content_type) = thread_type + Header(#http_head_connection) = "Keep-Alive" + Header(#http_head_keep_alive) = "timeout=15, max=1000" + thread_header = http_header_generate(Header()) + thread_buffer = AllocateMemory(thread_data_size+StringByteLength(thread_header)+12) + thread_buffer_offset = thread_buffer + thread_buffer_length = PokeS(thread_buffer_offset, thread_header,-1, #PB_UTF8|#PB_String_NoZero) : thread_buffer_offset + thread_buffer_length + Debug "Header Finished" + EndIf + + Default + ;************************************ + ;* + ;* Not a supported Command in HTTP Header Cleanup. + ;* + ;* Read Buffer to Memory and Clear Buffer to Zero until the complete Networkbuffer from this Client is Cleaned. + ;* + EndSelect - CopyMemory(thread_temp_cache_memory, thread_buffer_offset, thread_file_size) + CopyMemory(thread_temp_cache_memory, thread_buffer_offset, thread_data_size) FreeMemory(thread_temp_cache_memory) ; EndIf Debug "HTTP File Buffer Cleaned." ;Löschen des eingang Speichers. - FreeMemory(m_clients(Str(thread_cli_id))\datenbuffer()\Buffer) + If m_clients(Str(thread_cli_id))\datenbuffer()\Buffer > 0 + FreeMemory(m_clients(Str(thread_cli_id))\datenbuffer()\Buffer) + EndIf DeleteElement(m_clients(Str(thread_cli_id))\datenbuffer()) ;Daten Senden... - If SendNetworkData(thread_cli_id, thread_buffer , thread_file_size+(thread_buffer_offset-thread_buffer)) = thread_file_size+(thread_buffer_offset-thread_buffer) + If SendNetworkData(thread_cli_id, thread_buffer , thread_data_size+(thread_buffer_offset-thread_buffer)) = thread_data_size+(thread_buffer_offset-thread_buffer) ;Ok ;Debug "Gesendet:" + PeekS(thread_buffer,thread_buffer_length, #PB_Ascii) FreeMemory(thread_buffer) @@ -623,6 +811,7 @@ Module lhs_web EndSelect EndIf + ;} Until thread_alive = #False ResetList(m_clients(Str(thread_cli_id))\datenbuffer()) @@ -637,109 +826,10 @@ Module lhs_web DeleteMapElement(m_clients(), Str(thread_cli_id)) Debug "Thread Beendet." - ;} - EndProcedure - - Procedure FileCacheCleaner(Information.i) - Protected.i Selected - Protected.i Counter - Protected.i MaxSize = Information - Repeat - Delay(1000) ;Nur Alle Sekunden Prüfen - Counter + 1 - LockMutex(file_cache_mutex) - ResetMap(m_file_cache_map()) - ;Debug "FileCacheCleaner" + Str(Counter) + " Map Size:" + Str(MapSize(m_file_cache_map())) - If NextMapElement(m_file_cache_map()) - Repeat - m_file_cache(MapKey(m_file_cache_map()))\Timer - 1 - If m_file_cache(MapKey(m_file_cache_map()))\Timer <= 0 - If m_file_cache(MapKey(m_file_cache_map()))\Buffer > 0 - conf_cache_current - m_file_cache(MapKey(m_file_cache_map()))\Size - FreeMemory(m_file_cache(MapKey(m_file_cache_map()))\Buffer) - DeleteMapElement(m_file_cache(), MapKey(m_file_cache_map())) - Debug "Info:["+Str(innercount)+"] Killed:["+MapKey(m_file_cache_map())+"]" - Selected = DeleteMapElement(m_file_cache_map()) - Else - Debug "Info:["+Str(innercount)+"] Could Not kill:["+MapKey(m_file_cache_map())+"]" - EndIf - Else - Selected = NextMapElement(m_file_cache_map()) - EndIf - Until Selected = 0 - EndIf - - UnlockMutex(file_cache_mutex) - - - ForEver - - EndProcedure - - - Procedure FileCache(Information.i) - ;http://purearea.net/pb/english/manual/reference/ug_memory.html - Protected FileCacheCleanerThread.i - Protected Tempbuffer.i, MaxSize.i, Current.i - MaxSize = conf_cache_maxsize * 1024 * 1024 - FileCacheCleanerThread = CreateThread(@FileCacheCleaner(), MaxSize) - - Repeat - WaitSemaphore(file_cache_semaphore) - Debug "Adresse:"+m_file_cache()\Buffer - If (m_file_cache()\Size + conf_cache_current) <= MaxSize - conf_cache_current + m_file_cache()\Size - Tempbuffer = AllocateMemory(m_file_cache()\Size) - CopyMemory(m_file_cache()\Buffer, Tempbuffer, MemorySize(m_file_cache()\Buffer)) - m_file_cache()\Buffer = Tempbuffer - m_file_cache()\Timer = conf_cache_time - m_file_cache()\Is = #True - m_file_cache_map(MapKey(m_file_cache())) = #True - SignalSemaphore(file_cache_semaphore_thread) - Else - Debug "Cache Full" - SignalSemaphore(file_cache_semaphore_thread) - EndIf - - ForEver - EndProcedure - - Procedure AddFileToCache(MemoryID.i, FileName.s, Size.i) - LockMutex(file_cache_mutex) - Debug "Cache MaxSize:"+Str(conf_cache_maxsize*1024*1024)+" Actual Size:"+Str(conf_cache_current) - m_file_cache(FileName)\Buffer = MemoryID - m_file_cache(FileName)\Size = Size - SignalSemaphore(file_cache_semaphore) - WaitSemaphore(file_cache_semaphore_thread) - If m_file_cache(FileName)\Is - Debug FileName+" Size:"+Str(m_file_cache(FileName)\Size)+" Timer:"+Str(m_file_cache(FileName)\Timer)+ " new Memory ID:"+Str(m_file_cache(FileName)\Buffer) - Else - Debug "File not in cache was to full:"+FileName+ " MaxSize:"+Str(conf_cache_maxsize)+" Actual Size:"+Str(conf_cache_current/1024/1024) - EndIf - - UnlockMutex(file_cache_mutex) - EndProcedure - - Procedure.s GetFileFromCache(FileName.s, MemoryID.i) - Protected String.s - - If TryLockMutex(file_cache_mutex) - If m_file_cache(FileName)\Is - MemoryID = ReAllocateMemory(MemoryID, m_file_cache(FileName)\Size) - CopyMemory(m_file_cache(FileName)\Buffer, MemoryID, m_file_cache(FileName)\Size) - String.s = Str(m_file_cache(FileName)\Size) + ":" + Str(MemoryID) - Else - String.s = #error_string - EndIf - UnlockMutex(file_cache_mutex) - Debug "Get from cache: "+FileName+">"+String+"-------------------------------------------------------------<" - ProcedureReturn String.s - Else - ProcedureReturn #error_string - EndIf EndProcedure + Procedure.s mimetype(file.s) Select file Case "png" @@ -896,11 +986,275 @@ Module lhs_web EndProcedure - Procedure.s Work_Post_Header(InMemory.i) + Procedure.s Work_Post_Header(ContentLength.i, InMemory.i, Length.i) + + ;HEADER and Strings: + ; POST /login.mss HTTP/1.1 + ; Host: localhost:61000 + ; User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0 + ; Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 + ; Accept-Language: de,en-US;q=0.7,en;q=0.3 + ; Accept-Encoding: gzip, deflate + ; Referer: http://localhost:61000/index.html + ; Connection: keep-alive + ; Content-Type: application/x-www-form-urlencoded + ; Content-Length: 27 + ; + ; username=test&passwort=test + + ;********************************3 + ;* Script öffnen + ;* + ;* Akzeptierte Parameter: + ;* + ;* username + ;* passwort + ;* sid + ;* + ;* Identifikation und übergabe des paramters an script ... + ;Fixe Loginfunktion mit user und pasd + ; Position_String = FindString(Temp_String, "Content-Length:") + ; + ; If Position_String > 0 + ; ;Länge Content identifizieren. + ; + ; + ; EndIf + ; + ; While NextElement(HTTP_List()) + ; If HTTP_List()\ID = Event_Network_Client + ; HTTP_List()\Login = 1 + ; Break + ; Else + ; ;Do Nothing + ; + ; EndIf + ; Wend + + ;lweb_srv_mod_mss() + EndProcedure + + Procedure call_request(RequestString.s, Info.i = #get_handler_procedure) + Protected CurrPos, Count, Counter, PartString.s + If m_request(GetExtensionPart(RequestString))\routetype = #handler_type + Select Info + Case #get_handler_procedure + ProcedureReturn m_request(GetExtensionPart(RequestString))\call + Case #get_handler_prototype + ProcedureReturn m_request(GetExtensionPart(RequestString))\type + Default + ProcedureReturn 0 + EndSelect + EndIf + + If m_request(RequestString)\routetype = #handler_only Or m_request(RequestString)\routetype = #handler_sub + Select Info + Case #get_handler_procedure + ProcedureReturn m_request(RequestString)\call + Case #get_handler_prototype + ProcedureReturn m_request(RequestString)\type + Default + ProcedureReturn 0 + EndSelect + ElseIf m_request(GetExtensionPart(RequestString))\routetype = #handler_sub + Select Info + Case #get_handler_procedure + ProcedureReturn m_request(GetExtensionPart(RequestString))\call + Case #get_handler_prototype + ProcedureReturn m_request(GetExtensionPart(RequestString))\type + Default + ProcedureReturn 0 + EndSelect + EndIf + + ;Check auf Sub + Counter = CountString(RequestString, "/") + Count = 0 + CurrPos = 0 + While Count < Counter + CurrPos = FindString(RequestString, "/", 2 + CurrPos ) + PartString = Mid(RequestString, 1, CurrPos) + If m_request(PartString)\routetype = #handler_sub + Select Info + Case #get_handler_procedure + ProcedureReturn m_request(PartString)\call + Case #get_handler_prototype + ProcedureReturn m_request(PartString)\type + Default + ProcedureReturn 0 + EndSelect + EndIf + Count + 1 + Wend + + ProcedureReturn 0 + EndProcedure + + Procedure.s MapStringToJSONString(Map ConvertMap.s()) + Protected MyJSON + Protected.s Response + MyJSON = CreateJSON(#PB_Any) + If MyJSON + InsertJSONMap(JSONValue(MyJSON), ConvertMap()) + Response = ComposeJSON(MyJSON) + FreeJSON(MyJSON) + ProcedureReturn Response + EndIf + ProcedureReturn #error_string + EndProcedure + + Procedure.s Work_Post_ToJSON_x_www_form_urlencoded(ContentLength.i, MemorSize.i, Memory.i) + Define.s JSONString, Working, ContentString + Define CountParams, Count, JSON + Protected NewMap Posts.s() + + If ContentLength > 0 + OffsetMemory = Memory + (MemorSize - ContentLength) + ContentString = PeekS(OffsetMemory, ContentLength, #PB_UTF8) + ;Zerlegen & + ;Mapname = Content + If Len(ContentString) > 0 + CountParams = CountString(ContentString,"&") + If CountParams = 0 + If CountString(ContentString, "=") + Posts(StringField(ContentString, 1, "=")) = StringField(ContentString,2, "=") + Else + ProcedureReturn "" + EndIf + ElseIf CountParams > 0 + Count = 0 + Repeat + Posts(StringField(StringField(ContentString, Count + 1, "&"),1,"=")) = StringField(StringField(ContentString, Count + 1, "&"),2,"=") + ;Debug "Worked Count:"+Str(Count)+" of: "+Str(CountParams)+":"+StringField(StringField(ContentString, Count + 1, "&"),1,"=")+" = "+StringField(StringField(ContentString, Count + 1, "&"),2,"=") + Count + 1 + Until Count > CountParams + Else + ProcedureReturn "" + EndIf + JSON = CreateJSON(#PB_Any) + If JSON + InsertJSONMap(JSONValue(JSON), Posts()) + JSONString = ComposeJSON(JSON) + FreeMap(Posts()) + FreeJSON(JSON) + ProcedureReturn JSONString.s + Else + ProcedureReturn #error_string + EndIf + Else + ProcedureReturn "" + EndIf + Else + ProcedureReturn "" + EndIf EndProcedure - Procedure.s Work_Get_Header(InMemory.i) + Procedure.s register_client_handler(Route.s, Callback.i, AppPrototype.i = #handler_proto_get, RouteType.i = #handler_sub) + m_request(Route)\type = AppPrototype + m_request(Route)\call = Callback + m_request(Route)\routetype = RouteType + Debug "Handler Registriert:"+Route + EndProcedure + + ;Buggy FileCache Routines ... Wiso auch immer das noch nicht richtig Funktioniert. + + Procedure FileCacheCleaner(Information.i) + Protected.i Selected + Protected.i Counter + Protected.i MaxSize = Information + Repeat + Delay(1000) ;Nur Alle Sekunden Prüfen + Counter + 1 + LockMutex(file_cache_mutex) + ResetMap(m_file_cache_map()) + ;Debug "FileCacheCleaner" + Str(Counter) + " Map Size:" + Str(MapSize(m_file_cache_map())) + If NextMapElement(m_file_cache_map()) + Repeat + m_file_cache(MapKey(m_file_cache_map()))\Timer - 1 + If m_file_cache(MapKey(m_file_cache_map()))\Timer <= 0 + If m_file_cache(MapKey(m_file_cache_map()))\Buffer > 0 + conf_cache_current - m_file_cache(MapKey(m_file_cache_map()))\Size + FreeMemory(m_file_cache(MapKey(m_file_cache_map()))\Buffer) + DeleteMapElement(m_file_cache(), MapKey(m_file_cache_map())) + Debug "Info:["+Str(innercount)+"] Killed:["+MapKey(m_file_cache_map())+"]" + Selected = DeleteMapElement(m_file_cache_map()) + Else + Debug "Info:["+Str(innercount)+"] Could Not kill:["+MapKey(m_file_cache_map())+"]" + EndIf + Else + Selected = NextMapElement(m_file_cache_map()) + EndIf + Until Selected = 0 + EndIf + + UnlockMutex(file_cache_mutex) + + + ForEver + + EndProcedure + + Procedure FileCache(Information.i) + ;http://purearea.net/pb/english/manual/reference/ug_memory.html + Protected FileCacheCleanerThread.i + Protected Tempbuffer.i, MaxSize.i, Current.i + MaxSize = conf_cache_maxsize * 1024 * 1024 + FileCacheCleanerThread = CreateThread(@FileCacheCleaner(), MaxSize) + + Repeat + WaitSemaphore(file_cache_semaphore) + Debug "Adresse:"+m_file_cache()\Buffer + If (m_file_cache()\Size + conf_cache_current) <= MaxSize + conf_cache_current + m_file_cache()\Size + Tempbuffer = AllocateMemory(m_file_cache()\Size) + CopyMemory(m_file_cache()\Buffer, Tempbuffer, MemorySize(m_file_cache()\Buffer)) + m_file_cache()\Buffer = Tempbuffer + m_file_cache()\Timer = conf_cache_time + m_file_cache()\Is = #True + m_file_cache_map(MapKey(m_file_cache())) = #True + SignalSemaphore(file_cache_semaphore_thread) + Else + Debug "Cache Full" + SignalSemaphore(file_cache_semaphore_thread) + EndIf + + ForEver + EndProcedure + + Procedure AddFileToCache(MemoryID.i, FileName.s, Size.i) + LockMutex(file_cache_mutex) + Debug "Cache MaxSize:"+Str(conf_cache_maxsize*1024*1024)+" Actual Size:"+Str(conf_cache_current) + m_file_cache(FileName)\Buffer = MemoryID + m_file_cache(FileName)\Size = Size + SignalSemaphore(file_cache_semaphore) + WaitSemaphore(file_cache_semaphore_thread) + If m_file_cache(FileName)\Is + Debug FileName+" Size:"+Str(m_file_cache(FileName)\Size)+" Timer:"+Str(m_file_cache(FileName)\Timer)+ " new Memory ID:"+Str(m_file_cache(FileName)\Buffer) + Else + Debug "File not in cache was to full:"+FileName+ " MaxSize:"+Str(conf_cache_maxsize)+" Actual Size:"+Str(conf_cache_current/1024/1024) + EndIf + + UnlockMutex(file_cache_mutex) + EndProcedure + + Procedure.s GetFileFromCache(FileName.s, MemoryID.i) + Protected String.s + + If TryLockMutex(file_cache_mutex) + If m_file_cache(FileName)\Is + MemoryID = ReAllocateMemory(MemoryID, m_file_cache(FileName)\Size) + CopyMemory(m_file_cache(FileName)\Buffer, MemoryID, m_file_cache(FileName)\Size) + String.s = Str(m_file_cache(FileName)\Size) + ":" + Str(MemoryID) + Else + String.s = #error_string + EndIf + UnlockMutex(file_cache_mutex) + Debug "Get from cache: "+FileName+">"+String+"-------------------------------------------------------------<" + ProcedureReturn String.s + Else + ProcedureReturn #error_string + EndIf EndProcedure diff --git a/lweb_header.pbi b/lweb_header.pbi index d9d3196..8475b21 100644 --- a/lweb_header.pbi +++ b/lweb_header.pbi @@ -31,12 +31,37 @@ DeclareModule lhs_web #conf_cache_enable EndEnumeration - Enumeration cli_handler + Enumeration cli_handler 1 #handler_only ;Reagiert nur auf die Url (Bsp. /rest/v1/test aber nicht auf /rest/v1/test/sub) #handler_sub ;Reagiert auf eine ganze Url die begint. (Bsp. /rest/v1/test sowie auch auf /rest/v1/test/sub) - #handler_type ;Reagiert auf datentypen (zb. *.php) + #handler_type ;Reagiert auf datentypen (zb. *.php = "php") EndEnumeration + Enumeration e_handler_prototype 1 + #handler_proto_universal + #handler_proto_post + #handler_proto_get + EndEnumeration + + Enumeration cli_handler_app 1 + #app_handler_map_string ;.s App(Requested.s, Map HandlerMap.s()) + #app_handler_json ;.s App(Requested.s, JSONString.s) + #app_handler_string ;.s App(Requested.s, String.s) + #app_handler_ressource ;.s App(Requested.s) + EndEnumeration + + #response_string = "response_string" + #response_Memory = "response_memory" + + + ;cli_handler_app Return: + ;Map.s() to JSONString + #cha_R_ResponseType = "ResponseType" ;Element ResponseType :Mimetype + #cha_R_ResponseContentType = "ResponseContentType" ;Element ResponseContentType :#response_string , #response_Memory + #cha_R_MemoryAdress = "MemoryAdress" ;Element MemoryAdress :Converted to String + #cha_R_MemorySize = "MemorySize" ;Element MemorySize :Converted to String + #cha_R_StringBase64 = "StringBase64" ;Element StringBase64 :Base64 Encoded String + #cha_R_http_head_status = "HeaderStatus" ;200 OK 300 Error 500 Server Error usw. #error_string = "error" #http_head_method = "method" #http_head_request = "request" @@ -49,14 +74,16 @@ DeclareModule lhs_web #http_head_content_type = "content-type:" #http_head_connection = "connection:" #http_head_keep_alive = "keep-alive:" - + + #http_content_type_application_x_www_form_urlencoded = "application/x-www-form-urlencoded" + #http_content_type_multipart_form_data = "multipart/form-data" + Declare set_config(parameter.i=#conf_defaultfile, setting.s="index.html") Declare.s get_config(parameter.i=#conf_defaultfile) Declare start_server() - ;Declare.s register_client_handler(Route.s, Callback.i, Parameter.i = #handler_sub) - ;Declare.s register_client_handler_JSON(Route.s, Callback.i, JSONString.s, Parameter.i = #handler_sub) - - ;Declare register_client_handler(Type.s) + Declare.s register_client_handler(Route.s, Callback.i, AppPrototype.i = #handler_proto_get, RouteType.i = #handler_sub) + Declare.s mimetype(file.s) + Declare.s MapStringToJSONString(Map ConvertMap.s()) Declare IsIPStringValid(Adress.s) EndDeclareModule \ No newline at end of file diff --git a/server_example.pb b/server_example.pb index 75f9575..39b229f 100644 --- a/server_example.pb +++ b/server_example.pb @@ -24,8 +24,8 @@ lhs_web::set_config(lhs_web::#conf_port, "8099") ;lhs_web::set_config(lhs_web::#conf_binding, "127.0.0.1") lhs_web::set_config(lhs_web::#conf_binding, "0.0.0.0") -lhs_web::set_config(lhs_web::#conf_defaultfile, "index.html") -lhs_web::set_config(lhs_web::#conf_basedir, "/srv/www/htdocs/") +lhs_web::set_config(lhs_web::#conf_defaultfile, "/index.html") +lhs_web::set_config(lhs_web::#conf_basedir, "/home/renlin/testweb/") lhs_web::set_config(lhs_web::#conf_error400, "integrated") lhs_web::set_config(lhs_web::#conf_max_clients, "100") lhs_web::set_config(lhs_web::#conf_cache_enable, "0") @@ -33,7 +33,56 @@ lhs_web::set_config(lhs_web::#conf_cache_enable, "0") ;* includes ;* +Procedure.s Sample_Header(Map Header.s()) + Define *Text + Define.s Encoded, ZumSenden, TBD + NewMap Response.s() + TBD="

Header


"+#CRLF$ + ResetMap(Header()) + While NextMapElement(Header()) + TBD = TBD+"
"+MapKey(Header())+" : "+Header()+"

"+#CRLF$ + Wend + + *Text = UTF8("
Alle Client Headers
"+TBD+"") + Encoded = Base64Encoder(*Text, MemorySize(*Text)) + Debug "Encoded: " + Encoded + Response(lhs_web::#cha_R_ResponseContentType) = lhs_web::#response_string + Response(lhs_web::#cha_R_StringBase64) = Encoded + Response(lhs_web::#cha_R_ResponseType) = lhs_web::mimetype("html") + Response(lhs_web::#cha_R_http_head_status) = "200 OK" + ZumSenden = lhs_web::MapStringToJSONString(Response()) + Debug "ZumSenden: " + ZumSenden + ProcedureReturn ZumSenden +EndProcedure +Procedure.s Formular_Test(Map Header.s(), ContentData.s) + Define *Text + Define.s Encoded, ZumSenden, TBD + NewMap Response.s() + TBD="

FormularPost


"+#CRLF$ + ResetMap(Header()) + While NextMapElement(Header()) + TBD = TBD+"
"+MapKey(Header())+" : "+Header()+"

"+#CRLF$ + Wend + Debug "TBD:"+TBD + Debug "contentData:"+ContentData + *Text = UTF8("
Alle Client Headers
"+TBD+"

ConentJSON


"+ContentData+"") + Debug "Unencoded:"+PeekS(*Text, -1 , #PB_UTF8) + Encoded = Base64Encoder(*Text, MemorySize(*Text)) + Debug "Encoded: " + Encoded + Response(lhs_web::#cha_R_ResponseContentType) = lhs_web::#response_string + Response(lhs_web::#cha_R_StringBase64) = Encoded + Response(lhs_web::#cha_R_ResponseType) = lhs_web::mimetype("html") + Response(lhs_web::#cha_R_http_head_status) = "200 OK" + ZumSenden = lhs_web::MapStringToJSONString(Response()) + Debug "ZumSenden: " + ZumSenden + ProcedureReturn ZumSenden + +EndProcedure + +lhs_web::register_client_handler("/server/show_client_headers", @Sample_Header(),lhs_web::#handler_proto_get, lhs_web::#handler_sub) + +lhs_web::register_client_handler("/post_test", @Formular_Test(),lhs_web::#handler_proto_post, lhs_web::#handler_sub) ;* ;* Initialisierung Netzwerk @@ -49,7 +98,7 @@ Else EndIf If lhs_web::start_server() - Debug "Server gestartet" + Debug "Server gestartet" + lhs_web::get_config(lhs_web::#conf_port) Else Debug "Fehlgeschlagen" End @@ -57,6 +106,7 @@ EndIf counter = 0 OpenConsole("Test") +PrintN("Webserver an Port:"+lhs_web::get_config(lhs_web::#conf_port)) PrintN("Press Enter to Exit") Input()