Jump to content

Recommended Posts

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 post
Share on other sites
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

Link to post
Share on other sites

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 post
Share on other sites
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 by argumentum
Link to post
Share on other sites

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 post
Share on other sites

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 )

Link to post
Share on other sites
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.

Link to post
Share on other sites

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 ) :)  

Link to post
Share on other sites

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 post
Share on other sites

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.

Link to post
Share on other sites

ok, all done and functional :) 

#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 )

Edited by argumentum
better code
Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By uncommon
      So I have been reading on how to use OAuth2 Service accounts(https://developers.google.com/identity/protocols/OAuth2ServiceAccount) and ran into an issue with autoit I can not solve. To keep this short to communicate to the Google API I need to use a JSON Web signature containing
      {Base64url encoded header}.{Base64url encoded claim set}.

      The Header and claim set work fine but I do not know how to convert them to a byte array using autoit. I have looked around on the forums and website but have not found anything that seems to work. Here is my code.
      #include-once #include <Array.au3> #include <Constants.au3> #include <Debug.au3> #include <File.au3> #include "Json.au3" #include <Process.au3> #include <ProgressConstants.au3> #include <String.au3> #include <WindowsConstants.au3> #include "WinHttp.au3" #include <UnixTime.au3> #include <StringConstants.au3> Global $UnixTime = _TimeGetStamp() $sJWTheader = '{"alg":"RS256","typ":"JWT"}' $sJWTclaimset = '{"iss":"Removed@forprivacy.com","scope":"https://www.googleapis.com/auth/spreadsheets","aud":"https://www.googleapis.com/oauth2/v4/token","exp":' & $UnixTime + 3600 & ',"iat":' & $UnixTime & '}' Local $taiData = DllStructCreate("BYTE[256]") Local $sText = _base64($sJWTheader)&'.'&_base64($sJWTclaimset) DllStructSetData($taiData, 1, StringToBinary($sText, 4)) Local $bvResult = DllStructGetData($taiData, 1) $sJWTSigature = $bvResult $sJWT = _base64($sJWTheader)&'.'&_base64($sJWTclaimset)&'.'&_base64($sJWTSigature) $POSTHeader = "Content-Type: application/x-www-form-urlencoded" $hOpen = _WinHttpOpen() $hConnect = _WinHttpConnect($hOpen, "https://www.googleapis.com/") $sRead = _WinHttpSimpleSSLRequest($hConnect, "POST", "oauth2/v4/token", Default, "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" & $sJWT) _WinHttpCloseHandle($hConnect) _WinHttpCloseHandle($hOpen) ConsoleWrite($sRead & @LF) The whole bytes thing is beyond with what I know about auotit. I can't tell what I need to fix.
       
       
    • By Chris86
      Hello,
      I'm in the making of a script that needs to post XML data through HTTPS to our ServiceDesk Plus MSP service. I'm running into trouble because I have never made any scripts using HTTP with POST and I cannot see what I'm doing wrong here.
      Can someone help me or should I find help somewhere else?
      $URLSDP="" ;REMOVED - cannot publish this. this is the HTTPS url to our ServiceDesk $TechnicianKey="" ;REMOVED $ID="" ; Request ID $sPostData = "OPERATION_NAME=ADD_NOTE&INPUT_DATA=<Operation><Details><Notes><Note><isPublic>false</isPublic><notesText>Text added to the note</notesText></Note></Notes></Details></Operation>&format=XML" $oHTTP = ObjCreate("winhttp.winhttprequest.5.1") $oHTTP.Open("POST", $URLSDP & "/sdpapi/request/"& $ID & "/note?TECHNICIAN_KEY="& $TechnicianKey, False) $oHTTP.SetRequestHeader("Content-Type", "xml") $oHTTP.Send($sPostData) $oReceived = $oHTTP.ResponseText $oStatusCode = $oHTTP.Status ConsoleWrite($oStatusCode & @CRLF) ConsoleWrite($oReceived & @CRLF) If $oStatusCode = 200 Then ;Process the response $oReceived ;ConsoleWrite(@CRLF & "Response" & @CRLF & $oReceived & @CRLF) Else MsgBox(16, "Error " & $oStatusCode, $oReceived, 7) EndIf Response:
      <operation name="POST"><result><status>Failed</status><message>Invalid operation</message></result></operation>REST API guide.
      https://www.manageengine.com/products/service-desk-msp/help/adminguide/api/notes-operations.html#add
       
    • By armoros
      My friends would like to know how to use AutoIt to perform an HTTP POST request to a URL.

      For example in Virustotal there is a python script that does this exact action to send a url for scanning.

      is there a UDF that helps with this.

      Thank you for any help.
×
×
  • Create New...