kudrow Posted February 11, 2020 Author Share Posted February 11, 2020 I finally got around to testing this. I did not realize how bad the flu can be. Thank you for all of the help. It appears to work very well. There appears to be a problem from the sending application but I will confirm this and update you with my findings. Thanks again! Kud Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 1 minute ago, kudrow said: There appears to be a problem from the sending application ..this is a very loose implementation. The sending application may have other requirements that may be needed. 5 minutes ago, kudrow said: I will confirm this and update you with my findings Thanks Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
kudrow Posted February 11, 2020 Author Share Posted February 11, 2020 Well, it worked once but I cannot get it to work again. I have the sending application sending {{"name" : "test"}} and the results are. I tried both POST and PUT. It worked once with post. === start of # 2 ===>PUT / HTTP/1.1 Content-Type: application/json Host: removed Content-Length: 17 Expect: 100-continue Connection: Keep-Alive <=== end of # 2 === # 2 loaded at 13:41:59.837, responded in 110 ms. due to timeout === start of # 1 ===>POST / HTTP/1.1 Content-Type: application/json Host: removed Content-Length: 17 Expect: 100-continue Connection: Keep-Alive <=== end of # 1 === # 1 loaded at 13:38:31.243, responded in 131 ms. due to timeout Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 (edited) On 2/11/2020 at 2:44 PM, kudrow said: Expect: 100-continue ..that means that is sending this chunk and will continue, hence, should not close the socket and wait until done. ..this way they buy themselves time to avoid timeout while collecting the data they need to send. The Expect HTTP request header indicates expectations that need to be fulfilled by the server in order to properly handle the request. As is coded will fail inevitably as the code blindly closes the socket. I'm not an HTTP guy. Is not what I do all day nor am very familiar with, so, I'll need an application that does that, to continue coding and testing. And that will take me more than an hour. Edited February 12, 2020 by argumentum Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 https://evertpot.com/http/100-continue/ Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 I've updated the code to handle "100-continue". Try it out. See if just that does it. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
kudrow Posted February 11, 2020 Author Share Posted February 11, 2020 POST query2array: 1 / ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ === start of # 1 ===>POST / HTTP/1.1 Content-Type: application/json Host: removed Content-Length: 17 Expect: 100-continue Connection: Keep-Alive <=== end of # 1 === # 1 loaded at 14:39:43.618 - Socket: 680 - Received in 31.59 ms. - Responded in 2.12 ms. ( no data ) Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 ..updated again. Try that and play with the sleep() if it returns no data. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
kudrow Posted February 11, 2020 Author Share Posted February 11, 2020 ok now we have progress.. query2array: 1 / ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯=== start of # 1 ===>{"name" : "test"}<=== end of # 1 === # 1 loaded at 15:02:33.733 - Socket: 660 - Received in 629041841.95 ms. - Responded in 0.2 ms. ( no data ) argumentum 1 Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 24 minutes ago, kudrow said: Content-Length: 17 ..so it sends this 100-continue, probably to ask the server if is all good with it ( the server side, else send a "417 Expectation Failed" or some error, or "100 continue" ) and continue processing the 17 bytes of data that it will send. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
kudrow Posted February 11, 2020 Author Share Posted February 11, 2020 Whatever you did, is working consistently! argumentum 1 Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 Did changed the code to show the body's data, that is now the whole buffer after the 100-continue, to show in the array. Do post the result, even if functional, for future reference for anyone reading this post ( with the URL changed to a fake ) Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
kudrow Posted February 11, 2020 Author Share Posted February 11, 2020 Well it does not work now... POST query2array: 1 / POST / HTTP/1.1 Content-Type: application/json Host: removed Content-Length: 18 Expect: 100-continue Connection: Keep-Alive ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ === start of # 1 ===>POST / HTTP/1.1 Content-Type: application/json Host: removed Content-Length: 18 Expect: 100-continue Connection: Keep-Alive <=== end of # 1 === # 1 loaded at 15:51:24.488 - Socket: 664 - Received in 31.37 ms. - Responded in 2 ms. ( no data ) Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 hmmm, based on the "=== start of # 1 ===>{"name" : "test"}<=== end of # 1 ===" response you had before, it should have worked ..I don't find the error in the code ( ..just running code in my head, ..give me time ) If you can control-z In SciTE until the code that did run ok, post a longer log, say, 5 sendings. You can remove the posting later, to save forum resources. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 ..edited the post to better debug. Try that. Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
argumentum Posted February 11, 2020 Share Posted February 11, 2020 (edited) ok, all done and functional expandcollapse popup#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #include <MsgBoxConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <Debug.au3> #include <SQLite.au3> ;~ #include <ZLIB.au3> ; https://www.autoitscript.com/forum/topic/128962-zlib-deflateinflategzip-udf/?Do=findComment&comment=1312432 ;~ #include <Crypt.au3> ; for "Content-MD5:" Global $tcp_Home = "home/" ; change this home page as needed, if this default bothers your testing. Global $tcp_Port = 80 ; if 80 is in use, try 81, as in http://127.0.0.1:81/home/ Global $tcp_ListenSocket = 0, $tcp_aForm[] = [755, 400, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, "", 1] TCPStartup() ; Start the TCP service. OnAutoItExitRegister("OnAutoItExit") ; Register OnAutoItExit to be called when the script is closed. mainloop() ; tha MAIN loop Func mainloop() Opt("TCPTimeout", 20) ; ..I coded this with Opt("GUIOnEventMode", 0), wrong choice, so, lets TCPTimeout faster :/ ; Do change the timeout if you get connection problems. mainGui() Local $iCountTryCloseSocket, $iTestSocketCount = 0, $iError = 0, $buffer, $bReceived ; Assign a variable the socket and bind to the IP Address and Port specified with a maximum of 100 pending connexions. $tcp_ListenSocket = TCPListen("0.0.0.0", $tcp_Port, 100) If @error Then $iError = @error ; Someone is probably already listening on this IP Address and Port (script already running?). MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", "Server:" & @CRLF & "Could not listen, Error code: " & $iError, 60, $tcp_aForm[3]) Return False EndIf Local $sDueToTimeout, $sTimer, $hTimerResponse, $hTimerReqRcvd, $errTCPRecv, $extTCPRecv, $myHRseparator = @CRLF & "<hr>" & @CRLF & @CRLF Local $sTime, $urlReq, $method, $ContentLength, $ContentBody, $ContentType, $Referer, $AcceptEncoding, $Header_Expect, $iSocket = -1 ; Assign a Local variable to be used by the Client socket. GUICtrlSetData($tcp_aForm[4], "do search" & @CRLF & @CRLF & _ "https://www.google.com/search?q=http+protocol" & @CRLF & _ "https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol" & @CRLF & @CRLF & _ "to get acquainted with the protocol if you need to.") Local $sNiceLineBottom = Chr(0xAF) Local $sNiceLineTop = "_" For $n = 1 To 6 $sNiceLineBottom &= $sNiceLineBottom $sNiceLineTop &= $sNiceLineTop Next Local $aQueryData[1][5] = [[0]] Local $bMyFaviconFile = MyFaviconFile() ; ..it looks nicer with a favicon If FileGetSize(@ScriptDir & "\favicon.ico") Then $bMyFaviconFile = FileRead(@ScriptDir & "\favicon.ico") While 1 If $iSocket = -1 Then $iSocket = TCPAccept($tcp_ListenSocket) ; here it will wait the declared "Opt('TCPTimeout', 20)" If @error Then ; If an error occurred display the error code and return False. $iError = @error MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", "Server:" & @CRLF & "Could not accept the incoming connection, Error code: " & $iError) Return False EndIf If $iSocket <> -1 Then ; SideNote: I would fork the socket right here but this is just an example $hTimerReqRcvd = TimerInit() ; so is better to not over complicate it. $iTestSocketCount += 1 ContinueLoop ; As there is a socket, lets attend to it ASAP. EndIf Else $buffer = StringToBinary("") Do $sDueToTimeout = "" $bReceived = TCPRecv($iSocket, 4096, 1) ; $TCP_DATA_BINARY (1) - return binary data $errTCPRecv = @error $extTCPRecv = @extended $buffer &= $bReceived If DiscernData(BinaryToString($buffer), $urlReq, $method, $ContentLength, $ContentBody, $ContentType, $Referer, $aQueryData, $AcceptEncoding, $Header_Expect) Then ExitLoop $sDueToTimeout = " due to timeout" ; tho, if there is no DiscernData(), it could just be skipped Until $extTCPRecv Or $bReceived = "" ; but since this is an example, it shows that there was a connection ( good for debugging ) If $bReceived = "" Then $sDueToTimeout = " ( no data )" $hTimerReqRcvd = Round(TimerDiff($hTimerReqRcvd), 2) $hTimerResponse = TimerInit() $buffer = BinaryToString($buffer) If $errTCPRecv Then TCPCloseSocket($iSocket) ; Close the socket. $iSocket = -1 GUICtrlSetData($tcp_aForm[4], @CRLF & @TAB & "# " & $iTestSocketCount & " @error,@extended = " & $errTCPRecv & "," & $extTCPRecv & @CRLF & @CRLF & GUICtrlRead($tcp_aForm[4])) ElseIf $buffer = "" And $tcp_aForm[6] = 1 Then TCPCloseSocket($iSocket) ; Close the socket. $iSocket = -1 Else $sTime = @HOUR & ":" & @MIN & ":" & @SEC & "." & @MSEC If $Header_Expect = 100 Then HttpSender($iSocket, $AcceptEncoding, "", "text/html", "100 Continue") GUICtrlSetData($tcp_aForm[4], "=== start of # " & $iTestSocketCount & "(TBC) ===>" & $buffer & "<=== end of # " & $iTestSocketCount & _ "(TBC) ===" & @CRLF & @TAB & "# " & $iTestSocketCount & " loaded at " & $sTime & $sTimer & $sDueToTimeout & @CRLF & @CRLF & GUICtrlRead($tcp_aForm[4])) Sleep(200) ; give it some time... try more if needed for this hack. $hTimerReqRcvd = TimerInit() ContinueLoop ; ..skip the TCPCloseSocket() this once. ; https://httpstatusdogs.com/100-continue ; https://evertpot.com/http/100-continue/ ; tested with: ; >curl -v -i -H "Expect: 100-continue" -H "Content-Type: application/json" -d "{\"Firstname\":\"Mickey\",\"Lastname\":\"Mouse\"}" http://127.0.0.1:80/< ; >curl -v -i -H "Expect: 100-continue" -H "Content-Type: text/plain" -d "firstname=Mickey&lastname=Mouse" http://127.0.0.1:80/< ElseIf $method = "GET" And $urlReq = "/" & $tcp_Home Then ; show the "index.html" HttpSender($iSocket, $AcceptEncoding, '<!DOCTYPE html><html><head>' & @CRLF & _ '<script src="http://code.jquery.com/jquery-latest.min.js"></script>' & @CRLF & _ '</head>' & @CRLF & '<body bgcolor="#CCCCCC">' & @CRLF & _ forumLink() & $myHRseparator & _ '<a name="t1"></a>' & formPut() & $myHRseparator & _ '<a name="t2"></a>' & formSample('POST') & $myHRseparator & _ '<a name="t3"></a>' & formSample('GET') & $myHRseparator & _ '<a name="t4"></a>' & formSample('PUT', 'enctype="application/json"') & $myHRseparator & _ ; https://darobin.github.io/formic/specs/json/ but the browser does not do as expected by the page creator '<div align="right">( Loaded at ' & $sTime & ' )</div></body>' & @CRLF & '</html>' & @CRLF) ; ... you'll have to read a lot to get why =( ElseIf $method = "GET" And $urlReq = "/favicon.ico" Then ; send the favicon.ico file HttpSender($iSocket, $AcceptEncoding, $bMyFaviconFile, "application/octet-stream") ; this is just for this example of PUT JSON ElseIf $method = "PUT" And StringInStr($ContentType, "application/json") And StringInStr($Referer, "/" & $tcp_Home) Then $ContentBody = StringReplace($ContentBody, '"Firstname"', '"From":"AutoIt v. ' & @AutoItVersion & '","Firstname"') ; ..changed something above, just for fun ;) TCPSend($iSocket, "HTTP/1.1 201 Created" & @CRLF & _ ; ..could have used HttpSender(), but is all the same. This is a learning script :) "Content-Length: " & StringLen($ContentBody) & @CRLF & _ "Content-Type: " & $ContentType & @CRLF & @CRLF & _ $ContentBody) ; meh, just returning someting to parse. You may need '{"status":"OK"}' or what not ; ; but in this example, it expects this data, else you may see in the browser's ConsoleLog() an error. Else ; below is sending this, as is pertinent to this "index.html" and of no concecuence overall BUT should be otherwise properly handled. TCPSend($iSocket, "HTTP/1.0 200 OK" & @CRLF & _ "Content-Type: text/html" & @CRLF & @CRLF & _ "<!DOCTYPE html><html><body bgcolor=""#CCCCCC""><script> function goBack() { window.history.back(); } </script>" & _ "<button onclick=""goBack()""> The browser is waiting for a response, so, here is a button to go back to the prior page </button><br><br>" & _ "( Loaded at " & $sTime & " )</body></html>") EndIf $sTimer = " - Socket: " & $iSocket & " - Received in " & $hTimerReqRcvd & " ms. - Responded in " & Round(TimerDiff($hTimerResponse), 2) & " ms. " $iCountTryCloseSocket = 0 Do If $iCountTryCloseSocket Then Sleep(10) $iCountTryCloseSocket += 1 If $iCountTryCloseSocket > 5 Then ExitLoop Until TCPCloseSocket($iSocket) $iSocket = -1 $Header_Expect = 0 ; ..this here is just in case, the 100-continue timed out. If Not ($method = "GET" And $urlReq = "/favicon.ico" And $tcp_aForm[8] = 1) Then ; skip showing the favicon.ico request GUICtrlSetData($tcp_aForm[4], "=== start of # " & $iTestSocketCount & " ===>" & $buffer & "<=== end of # " & $iTestSocketCount & _ " ===" & @CRLF & @TAB & "# " & $iTestSocketCount & " loaded at " & $sTime & $sTimer & $sDueToTimeout & @CRLF & @CRLF & GUICtrlRead($tcp_aForm[4])) EndIf ;~ If $aQueryData[0][0] Then _DebugArrayDisplay($aQueryData, $method) ; ..if you want to see the array ( but this will block the script ) If $aQueryData[0][0] Then GUICtrlSetData($tcp_aForm[4], Display2DResult($method, $aQueryData, $sNiceLineTop, $sNiceLineBottom) & GUICtrlRead($tcp_aForm[4])) Dim $aQueryData[1][5] = [[0]] ; lets reset the query array, etc., $method = "" ; as we don't want to have the data lingering. $urlReq = "" $ContentLength = 0 $ContentBody = "" $ContentType = "" $Referer = "" $AcceptEncoding = "" $Header_Expect = "" EndIf EndIf If TimerDiff($hTimerResponse) < 500 Then ContinueLoop ; as there may be "100 pending connexions", this would make it more responsive, in theory. Switch GUIGetMsg() ; ..I should have code it OnEvent :( Case $GUI_EVENT_CLOSE GUIDelete() ExitLoop Case $tcp_aForm[9] ShellExecute("http://127.0.0.1:" & $tcp_Port & "/" & $tcp_Home) GUICtrlSetState($tcp_aForm[4], $GUI_FOCUS) Case $tcp_aForm[10] GUICtrlSetData($tcp_aForm[4], "") GUICtrlSetState($tcp_aForm[4], $GUI_FOCUS) Case $tcp_aForm[5] $tcp_aForm[6] = GUICtrlRead($tcp_aForm[5]) GUICtrlSetState($tcp_aForm[4], $GUI_FOCUS) Case $tcp_aForm[7] $tcp_aForm[8] = GUICtrlRead($tcp_aForm[7]) GUICtrlSetState($tcp_aForm[4], $GUI_FOCUS) Case $tcp_aForm[12] If GUICtrlRead($tcp_aForm[12]) = 1 Then WinSetOnTop($tcp_aForm[3], "", 1) Else WinSetOnTop($tcp_aForm[3], "", 0) EndIf EndSwitch WEnd EndFunc ;==>mainloop Func Display2DResult(ByRef $method, ByRef $aQueryData, ByRef $sNiceLineTop, ByRef $sNiceLineBottom) Local $sTop = $method & " query2array: " $sTop &= StringTrimLeft($sNiceLineTop, StringLen($sTop)) Return @CRLF & $sTop & @CRLF & _SQLite_Display2DResult($aQueryData, 0, True) & $sNiceLineBottom & @CRLF EndFunc ;==>Display2DResult ; example of a "more proper" way to send back a response Func HttpSender(ByRef $iSocket, ByRef $AcceptEncoding, $sHtmlText, $ContentType = "text/html", $ResponseStatusCode = "200 OK") #forceref $AcceptEncoding Local $bHtmlText = Binary($sHtmlText) Local $_Head = "HTTP/1.1 " & $ResponseStatusCode & @CRLF ; https://en.wikipedia.org/wiki/List_of_HTTP_status_codes $_Head &= "Content-Type: " & $ContentType & @CRLF ; ..so the client knows what is getting. $_Head &= "Connection: close" & @CRLF ; SideNote: ..an interaction takes 10 ms., is there a need for "muti-concurrent" code ? ;) ;~ If StringInStr($AcceptEncoding, "gzip") Then ; If you "#include <ZLIB.au3>" then you can compress the data. ;~ $bHtmlText = _ZLIB_GZCompress($bHtmlText, 5) ; It takes longer ( say 5 ms. ) to compress but on a slow connection, delivery is faster. ;~ $_Head &= "Content-Encoding: gzip" & @CRLF ; ..tho, if sending a compressed file, say a ZIP file, this is not to be used. ;~ EndIf $_Head &= "Content-Lenght: " & BinaryLen($bHtmlText) & @CRLF ; this is a must for binary file send. Might as well use it for everything. ;~ $_Head &= "Content-MD5: " & _Base64Encode(_Crypt_HashData($bHtmlText, $CALG_MD5)) & @CRLF ; https://tools.ietf.org/html/rfc1864 ; obsolete, https://stackoverflow.com/questions/8300471/is-content-md5-field-in-the-http-response-universal $_Head &= @CRLF ; end of header marker $_Head = Binary($_Head) TCPSendBinary($iSocket, $_Head) ; Send the head TCPSendBinary($iSocket, $bHtmlText) ; Send the body ( html or file, as delimited by the header ) EndFunc ;==>HttpSender Func TCPSendBinary(ByRef $hSocket, ByRef $bData) Local $iBytesSent, $iBytesTotalSent = 0, $iBytesTotal = BinaryLen($bData) Do $iBytesSent = TCPSend($hSocket, $bData) If @error Then ExitLoop $iBytesTotalSent += $iBytesSent $bData = BinaryMid($bData, $iBytesSent + 1, BinaryLen($bData) - $iBytesSent) Until 0 = BinaryLen($bData) Return SetError(0, Int($iBytesTotalSent <> $iBytesTotal), $iBytesTotalSent) EndFunc ;==>TCPSendBinary Func DiscernData($buffer, ByRef $urlReq, ByRef $method, ByRef $ContentLength, ByRef $ContentBody, ByRef $ContentType, ByRef $Referer, ByRef $aQueryData, ByRef $AcceptEncoding, ByRef $Header_Expect) If $Header_Expect = 100 Then ; we were waiting for the body ( $ContentLength ) $Header_Expect = 0 ; as we are not waiting anymore. $aQueryData = DiscernQuery2array($urlReq, $buffer, $ContentType) ; data is the $buffer. $urlReq and $ContentType should be the same as it hasn't changed. Return $ContentLength = StringLen($buffer) ; ..to flag that is all good. EndIf $urlReq = "" ; $aQueryData holds the query as an array but a JSON string will need extra coding. $method = "" ; If the $aQueryData[1][1] = "" then is likely a JSON string $Header_Expect = 0 $ContentLength = 0 ; In this example, at first, all I wanted, is to know that the data sent is full $ContentBody = "" ; and complete, to speed up the loop ( and save about 100 ms. of timeout ) Local $_Head = StringLeft($buffer, StringInStr($buffer, @CRLF & @CRLF) - 1) $ContentBody = StringTrimLeft($buffer, StringLen($_Head) + 4) Local $_BodyLen = StringLen($ContentBody) Local $aCRLF = StringSplit($_Head, @CRLF, 1) If UBound($aCRLF) < 2 Then Return Local $aSpaceSplit = StringSplit($aCRLF[1], " ") If UBound($aSpaceSplit) < 3 Then Return $method = $aSpaceSplit[1] $urlReq = $aSpaceSplit[2] For $n = 2 To $aCRLF[0] $aSpaceSplit = StringSplitHeader($aCRLF[$n]) If UBound($aSpaceSplit) < 3 Then ContinueLoop If $aSpaceSplit[1] = "Content-Length" Then $ContentLength = Int($aSpaceSplit[2]) ; tis should be equal to StringLen($ContentBody) If $aSpaceSplit[1] = "Content-Type" Then $ContentType = $aSpaceSplit[2] ; Discern if is JSON, when needed If $aSpaceSplit[1] = "Referer" Then $Referer = $aSpaceSplit[2] ; Discern if is from "/home/" If $aSpaceSplit[1] = "Accept-Encoding" Then $AcceptEncoding = $aSpaceSplit[2] ; Discern if to use GZIP If $aSpaceSplit[1] = "Expect" Then $Header_Expect = Int($aSpaceSplit[2]) ; Discern if Expect: 100-continue ;~ ; https://www.ietf.org/rfc/rfc1864.txt ; https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html ;~ If $aSpaceSplit[1] = "Content-MD5" Then $Header_Content_MD5 = _Base64Decode($aSpaceSplit[2]) ; Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== Next If $Header_Expect = 100 Then Return 1 ; ..there is no data at this point, just header. Switch $method Case "GET" $aQueryData = DiscernQuery2array($urlReq, Default, $ContentType) ; data is part of the URL Case Else $aQueryData = DiscernQuery2array($urlReq, $ContentBody, $ContentType) ; data is part of the body EndSwitch Return $ContentLength = $_BodyLen And StringLen($method) EndFunc ;==>DiscernData Func StringSplitHeader($sStr) Local $i = StringInStr($sStr, ":") If Not $i Then Return SetError(0, 2, "") ; the https://tools.ietf.org/html/rfc2616 talks about the ":" Local $a[3] = [2, "", ""] ; but not the space next to it as a delimiter of the field. $a[1] = StringLeft($sStr, $i - 1) ; To avoid mishaps, this function takes care of it. $a[2] = StringStripWS(StringTrimLeft($sStr, $i), 3) Return SetError(0, ($a[2] = "" ? 1 : 0), $a) EndFunc ;==>StringSplitHeader Func DiscernGetFragment($urlReq) Local $a = StringSplit($urlReq, "#") If UBound($a) < 3 Then Return "" Return $a[$a[0]] EndFunc ;==>DiscernGetFragment Func DiscernQuery2array(ByRef $urlReq, $ContentBody = Default, $ContentType = Default) Local $sContentPath, $sContentQuery Local $sFragment = DiscernGetFragment($urlReq) If $ContentBody = Default Then Local $aQueryData[1][5] = [[0, "", $sFragment, $ContentType, ""]] Local $i = StringInStr($urlReq, "?") If Not $i Then Return $aQueryData $sContentPath = StringLeft($urlReq, $i - 1) $sContentQuery = StringTrimLeft($urlReq, $i) ElseIf StringInStr($ContentType, "application/json") Then Local $aQuery[2][5] = [[1, $urlReq, $sFragment, $ContentType, ""], [$ContentBody, "", "", "", ""]] Return $aQuery ; is a json string, so better not StringSplit() it. Else $sContentPath = $urlReq $sContentQuery = $ContentBody EndIf Local $b, $n, $a = StringSplit($sContentQuery, "&") $i = 0 Local $aQuery[UBound($a) + 1][5] $aQuery[0][3] = $ContentType ; for debug For $n = 1 To UBound($a) - 1 $b = StringSplit($a[$n], "=") $i += 1 If $b[0] > 0 Then $aQuery[$i][0] = $b[1] ; name If $b[0] > 1 Then $aQuery[$i][1] = $b[2] ; value Next $aQuery[0][0] = $i ; this holds the name=value count $aQuery[0][1] = $sContentPath ; this holds the path ( minus the query if is a GET mathod ) $aQuery[0][2] = $sFragment ; this would hold the fragment ( https://en.wikipedia.org/wiki/Fragment_identifier ) ReDim $aQuery[$i + 1][5] Return $aQuery EndFunc ;==>DiscernQuery2array Func formSample($method = 'POST', $enctype = "") ; method an be GET or POST ; example from https://www.w3schools.com/html/html_forms.asp Local $sForm = '<h2>HTML Form ' & $method & '</h2>' & @CRLF $sForm &= '<p>This form will be submitted using the ' & $method & ' method:</p>' & @CRLF $sForm &= '<form ' & $enctype & ' action="/action_page_' & $method & '/" method="' & $method & '">' & @CRLF $sForm &= ' First name:<br>' & @CRLF $sForm &= ' <input type="text" name="firstname" value="Mickey">' & @CRLF $sForm &= ' <br>' & @CRLF $sForm &= ' Last name:<br>' & @CRLF $sForm &= ' <input type="text" name="lastname" value="Mouse">' & @CRLF $sForm &= ' <br><br>' & @CRLF $sForm &= ' <input type="submit" value="Submit">' & @CRLF $sForm &= '</form> ' & @CRLF $sForm &= '<p>If you click the "Submit" button, the form-data will be sent to a page called "/action_page_' & $method & '/".</p>' & @CRLF Return $sForm EndFunc ;==>formSample Func formPut() Local $sForm = '<!-- excerpt from http://exceptionallyexceptionalexceptions.blogspot.com/2011/12/convert-html-form-to-json.html -->' & @CRLF $sForm &= ' <h2>Create a JSON object that is nested/structured from a form and PUT via AJAX</h2>' & @CRLF $sForm &= '' & @CRLF $sForm &= ' <form id="form">' & @CRLF $sForm &= ' First name:<br>' & @CRLF $sForm &= ' <input id="meh.Firstname" type="text" name="meh.Firstname" placeholder="meh Mickey..." value="Mickey"><br>' & @CRLF $sForm &= ' Last name:<br>' & @CRLF $sForm &= ' <input id="meh.Lastname" type="text" name="meh.Lastname" placeholder="meh Mouse..." value="Mouse"><br><br>' & @CRLF $sForm &= ' <input id="input" type="submit" name="submit" value="Submit">' & @CRLF $sForm &= ' </form>' & @CRLF $sForm &= '<p>If you click the "Submit" button, the form-data will be sent in the background to a page called "/action_page_PUTviaAJAX/".</p>' & @CRLF $sForm &= '' & @CRLF $sForm &= ' <script>' & @CRLF $sForm &= ' $.fn.formToJSON = function() {' & @CRLF $sForm &= ' var objectGraph = {};' & @CRLF $sForm &= '' & @CRLF $sForm &= ' function add(objectGraph, name, value) {' & @CRLF $sForm &= ' if(name.length == 1) {' & @CRLF $sForm &= ' //if the array is now one element long, we''re done' & @CRLF $sForm &= ' objectGraph[name[0]] = value;' & @CRLF $sForm &= ' }' & @CRLF $sForm &= ' else {' & @CRLF $sForm &= ' //else we''ve still got more than a single element of depth' & @CRLF $sForm &= ' if(objectGraph[name[0]] == null) {' & @CRLF $sForm &= ' //create the node if it doesn''t yet exist' & @CRLF $sForm &= ' objectGraph[name[0]] = {};' & @CRLF $sForm &= ' }' & @CRLF $sForm &= ' //recurse, chopping off the first array element' & @CRLF $sForm &= ' add(objectGraph[name[0]], name.slice(1), value);' & @CRLF $sForm &= ' }' & @CRLF $sForm &= ' };' & @CRLF $sForm &= ' //loop through all of the input/textarea elements of the form' & @CRLF $sForm &= ' //this.find(''input, textarea'').each(function() {' & @CRLF $sForm &= ' $(this).children(''input, textarea'').each(function() {' & @CRLF $sForm &= ' //ignore the submit button' & @CRLF $sForm &= ' if($(this).attr(''name'') != ''submit'') {' & @CRLF $sForm &= ' //split the dot notated names into arrays and pass along with the value' & @CRLF $sForm &= ' add(objectGraph, $(this).attr(''name'').split(''.''), $(this).val());' & @CRLF $sForm &= ' }' & @CRLF $sForm &= ' });' & @CRLF $sForm &= ' return JSON.stringify(objectGraph);' & @CRLF $sForm &= ' };' & @CRLF $sForm &= '' & @CRLF $sForm &= ' $.ajaxSetup({' & @CRLF $sForm &= ' contentType: "application/json; charset=utf-8",' & @CRLF $sForm &= ' dataType: "json"' & @CRLF $sForm &= ' });' & @CRLF $sForm &= '' & @CRLF $sForm &= ' $(document).ready(function(){' & @CRLF $sForm &= ' $(''#input'').click(function() {' & @CRLF $sForm &= ' var send = $("#form").formToJSON();' & @CRLF $sForm &= ' $.ajax({' & @CRLF $sForm &= ' url: "/action_page_PUTviaAJAX/",' & @CRLF $sForm &= ' type: "PUT",' & @CRLF $sForm &= ' data: send,' & @CRLF $sForm &= ' error: function(xhr, error) {' & @CRLF $sForm &= ' alert(''Error!\n Status = '' + xhr.status + ''\n Message = '' + error);' & @CRLF $sForm &= ' },' & @CRLF $sForm &= ' success: function(data) {' & @CRLF ; if the msgBox in the browser bugs you, you can remove these lines $sForm &= ' alert(''Firstname = '' + data.meh.Firstname + ''\nLastname = '' + data.meh.Lastname + ''\nFrom = '' + data.meh.From );' & @CRLF $sForm &= ' }' & @CRLF $sForm &= ' });' & @CRLF $sForm &= ' return false;' & @CRLF $sForm &= ' });' & @CRLF $sForm &= ' });' & @CRLF $sForm &= ' </script>' & @CRLF Return $sForm EndFunc ;==>formPut Func forumLink() Return @CRLF & @CRLF & '..this is from the <a href="https://www.autoitscript.com/forum/topic/' & _ '201673-json-http-post-serverlistener/?do=findComment&comment=1447447" target="_blank">AutoIt forum</a> ( Test: ' & _ '<a href="#t1">JSON</a>, <a href="#t2">POST</a>, <a href="#t3">GET</a>, <a href="#t4">PUT</a> )' & _ @CRLF EndFunc ;==>forumLink Func MyFaviconFile() ; "https://stackoverflow.com/questions/2268204/favicon-dimensions/48646940" for more info. Return BinaryToString("0x00000100010010101000010004002801000016000000280000001000000020000000010004000000000080000000000000000000000010000000000000000000000000F4FF0000D6E0000000000000" & _ "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111000110020020020110010200200200201001002002002002100120" & _ "0200200200100102002002002010010020020020021001200200200200100102002002002010010020020020021001200200200200100102002002002010011020020020011000111111111111000000000000000000C00300" & _ "008001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080010000C0030000") EndFunc ;==>MyFaviconFile Func OnAutoItExit() ; Close the Listening socket to allow afterward binds. If $tcp_ListenSocket > 0 Then TCPCloseSocket($tcp_ListenSocket) TCPShutdown() ; Close the TCP service. EndFunc ;==>OnAutoItExit Func mainGui() #Region ### START Koda GUI section ### Form= $tcp_aForm[11] = Chr(160) & 'Example of a simple http deamon on "0.0.0.0" port ' & $tcp_Port $tcp_aForm[3] = GUICreate($tcp_aForm[11], $tcp_aForm[0], $tcp_aForm[1], (@DesktopWidth / 3) * 2, (@DesktopHeight / 2), BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX, $WS_SIZEBOX, $WS_THICKFRAME, $WS_TABSTOP), BitOR($WS_EX_TOPMOST, $WS_EX_WINDOWEDGE)) GUISetFont(10, 400, 0, "Courier New") If Not @Compiled Then SetExplicitAppUserModelID() $tcp_aForm[4] = GUICtrlCreateEdit("", 0, 35, $tcp_aForm[0], $tcp_aForm[1] - 35) GUICtrlSetLimit(-1, 0, 0) GUICtrlSetResizing(-1, $GUI_DOCKLEFT + $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKBOTTOM) $tcp_aForm[12] = GUICtrlCreateCheckbox("set TOPMOST", $tcp_aForm[0] - 750, 6, 145, 23, -1, $WS_EX_CLIENTEDGE) GUICtrlSetResizing(-1, $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT) GUICtrlSetState(-1, $GUI_CHECKED) $tcp_aForm[5] = GUICtrlCreateCheckbox("Skip timeout", $tcp_aForm[0] - 600, 6, 145, 23, -1, $WS_EX_CLIENTEDGE) GUICtrlSetResizing(-1, $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT) GUICtrlSetTip(-1, "skip showing TCPRecv()" & @CR & " timeout waiting for data.") $tcp_aForm[7] = GUICtrlCreateCheckbox("Skip /favicon", $tcp_aForm[0] - 450, 6, 145, 23, -1, $WS_EX_CLIENTEDGE) GUICtrlSetResizing(-1, $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT) GUICtrlSetTip(-1, "skip showing /favicon.ico" & @CR & " GET requests.") $tcp_aForm[9] = GUICtrlCreateButton("go to home site", $tcp_aForm[0] - 300, 5, 145, 25) GUICtrlSetResizing(-1, $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT) $tcp_aForm[10] = GUICtrlCreateButton("clear data", $tcp_aForm[0] - 150, 5, 145, 25) GUICtrlSetResizing(-1, $GUI_DOCKRIGHT + $GUI_DOCKTOP + $GUI_DOCKWIDTH + $GUI_DOCKHEIGHT) GUISetState(@SW_SHOW) #EndRegion ### END Koda GUI section ### EndFunc ;==>mainGui Func SetExplicitAppUserModelID($sIconFilename = Default) DllCall('shell32.dll', 'long', 'SetCurrentProcessExplicitAppUserModelID', 'wstr', 'AutoIt3.' & @ScriptName) If $sIconFilename = Default Or $sIconFilename = "" Then $sIconFilename = StringLeft(@AutoItExe, StringInStr(@AutoItExe, "\", 0, -1)) & "Icons\MyAutoIt3_Red.ico" EndIf If Not FileGetSize($sIconFilename) Then Return GUISetIcon($sIconFilename) TraySetIcon($sIconFilename) EndFunc ;==>SetExplicitAppUserModelID Func _Base64Encode($input) ; https://www.autoitscript.com/forum/topic/81332-_base64encode-_base64decode/ $input = Binary($input) Local $struct = DllStructCreate("byte[" & BinaryLen($input) & "]") DllStructSetData($struct, 1, $input) Local $strc = DllStructCreate("int") Local $a_Call = DllCall("Crypt32.dll", "int", "CryptBinaryToString", _ "ptr", DllStructGetPtr($struct), _ "int", DllStructGetSize($struct), _ "int", 1, _ "ptr", 0, _ "ptr", DllStructGetPtr($strc)) If @error Or Not $a_Call[0] Then Return SetError(1, 0, "") ; error calculating the length of the buffer needed EndIf Local $a = DllStructCreate("char[" & DllStructGetData($strc, 1) & "]") $a_Call = DllCall("Crypt32.dll", "int", "CryptBinaryToString", _ "ptr", DllStructGetPtr($struct), _ "int", DllStructGetSize($struct), _ "int", 1, _ "ptr", DllStructGetPtr($a), _ "ptr", DllStructGetPtr($strc)) If @error Or Not $a_Call[0] Then Return SetError(2, 0, "") ; error encoding EndIf Return DllStructGetData($a, 1) EndFunc ;==>_Base64Encode Func _Base64Decode($input_string) Local $struct = DllStructCreate("int") Local $a_Call = DllCall("Crypt32.dll", "int", "CryptStringToBinary", _ "str", $input_string, _ "int", 0, _ "int", 1, _ "ptr", 0, _ "ptr", DllStructGetPtr($struct, 1), _ "ptr", 0, _ "ptr", 0) If @error Or Not $a_Call[0] Then Return SetError(1, 0, "") ; error calculating the length of the buffer needed EndIf Local $a = DllStructCreate("byte[" & DllStructGetData($struct, 1) & "]") $a_Call = DllCall("Crypt32.dll", "int", "CryptStringToBinary", _ "str", $input_string, _ "int", 0, _ "int", 1, _ "ptr", DllStructGetPtr($a), _ "ptr", DllStructGetPtr($struct, 1), _ "ptr", 0, _ "ptr", 0) If @error Or Not $a_Call[0] Then Return SetError(2, 0, "") ; error decoding EndIf Return DllStructGetData($a, 1) EndFunc ;==>_Base64Decode @kudrow, kindly add "100-continue" as a tag to the OP, for anyone with this problem to find a solution. PS: you can use this within Apache ( example here ) PS2: there is another example based on this code here to load in chrome browser as "--app=". Edited July 17, 2023 by argumentum new reference Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting. Link to comment Share on other sites More sharing options...
kudrow Posted February 12, 2020 Author Share Posted February 12, 2020 Works like a champ!!! Updated the tags! Thanks a million @argumentum! argumentum 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now