Gemini 3 answer:
The reason your application "hangs" is specifically due to the third parameter in your Open method:
oHttp:Open( 'POST', cURL, .F. )
The .F. sets the request to Synchronous Mode. This means your application stops completely and waits for the server to reply. If the server is slow, or the network drops packets, your software will freeze indefinitely until a default system timeout occurs (which can take a long time).
Here are three concrete steps to fix this without rewriting your entire logic flow.
1\. Switch to ServerXMLHTTP
The standard Microsoft.XMLHTTP object is designed for old browser clients (like IE) and relies on the browser's cache and proxy settings. It is often unstable for application-to-application communication.
You should use MSXML2.ServerXMLHTTP. It is more robust, thread-safe, and most importantly, it allows you to set explicit timeouts.
2\. Set Explicit Timeouts
By switching to the server object, you can tell the system: "If you don't connect in 5 seconds, give up." This prevents the "eternal hang."
3\. Revised Code
Here is the improved version of your code. I have updated the object creation and added the timeout logic.
TRY
// Use ServerXMLHTTP for better reliability and timeout support
// We try 6.0 first (most modern), then fall back
TRY
oHttp := CreateObject( 'MSXML2.ServerXMLHTTP.6.0' )
CATCH
oHttp := CreateObject( 'MSXML2.ServerXMLHTTP' )
END
// VITAL: Set Timeouts (in milliseconds)
// Syntax: .setTimeouts( resolveTimeout, connectTimeout, sendTimeout, receiveTimeout )
// Example below: 5s to resolve DNS, 5s to connect, 15s to send, 15s to receive response
oHttp:setTimeouts( 5000, 5000, 15000, 15000 )
cURL := "https://..."
cCred := "user_id:password"
// ... (Your XML construction remains the same) ...
cHttpSend := [<?xml version="1.0"?>]
// ... code truncated for brevity ...
cHttpSend += [</soapenv:Envelope>]
// We keep .F. (Synchronous), but now it is safe because of the Timeouts above.
oHttp:Open( 'POST', cURL, .F. )
oHttp:SetRequestHeader( "Content-Type", "application/xml" )
oHttp:SetRequestHeader( "Accept", "*/*" )
oHttp:SetRequestHeader( "Authorization", "Basic " + hb_base64Encode(cCred) )
// The app will wait here, but ONLY for the duration of the timeout set above.
oHttp:Send( cHttpSend )
// Sysrefresh is useful to redraw the screen, but it only runs AFTER the send finishes.
Sysrefresh()
IF oHttp:status == 200
cWinResp := oHttp:ResponseBody
MEMOWRIT( Pdrivelog + cSdno + '.XML', cWinResp )
ELSE
// Handle HTTP errors (like 404 or 500) that are not exceptions
MEMOWRIT( Pdrivelog + 'WS-HTTP-ERROR-' + cSdno + '.LOG', 'HTTP Status: ' + Str(oHttp:status) + ' ' + oHttp:statusText )
ENDIF
CATCH oError
// This catches both Object creation errors AND Timeout errors
MEMOWRIT( Pdrivelog + 'WS-REQUEST-ERROR-' + cSdno + '.LOG', 'Unable to Complete Web Service' + cEol + oError:Description )
RETURN (.F.)
END
Summary of Changes
- Object: Changed to MSXML2.ServerXMLHTTP.6.0.
- Safety: Added oHttp:setTimeouts(...).
- Error Handling: Added an ELSE block to the status check. Sometimes a request "succeeds" technically (no crash) but returns a 500 Internal Server Error. You want to log that, too.
A Note on Sysrefresh()
You currently have Sysrefresh() after the oHttp:Send(). Because the request is synchronous (.F.), Sysrefresh() will not execute until the download is finished.
- If you want the UI to remain clickable/moving while downloading: You would need to change .F. to .T. (Asynchronous) and use an event handler (OnReadyStateChange). However, that is significantly more complex to implement in Harbour/xBase.
- Recommendation: Stick with the code above. The timeouts ensure that if the internet is slow, the app only "hangs" for 15 seconds (or whatever you set) rather than forever.
-----