Jump to content

Remy Lebeau

  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by Remy Lebeau

  1. Remy Lebeau

    TIDHTTPServer not receiving POST

    On a side note: Indy has logging components, such as TIdLogFile, that you can attach to each connection's IOHandler.Intercept property, such as in the server's OnConnect event. I would suggest doing that instead of rolling your own logging code. If, for no other reason, than because you are opening and closing the log file on every message logged, which is highly inefficient. There is no possible way that HeadersCanContinue() and CreatePostStream() would not be called for every request. So I have to assume that the changes you made to Indy's source code did not take effect, ie you did not recompile Indy, and then recompile your app with the updated library. If that is not the case, then something else is going on that I can't see. Obviously, OnCommandOther would not be called for a GET request, OnCommandGet would be called instead. At this point, your debugging efforts are not really helping. And I don't have a working environment that I can test with. So, we are at a stalemate. Can you, at least, provide a complete log or Wireshark capture of the entire HTTP conservsation that is not working for you? Also, are you using plain HTTP or secure HTTPS? You said originally that the problem did not start happening until you made a modification to your web server - what was that modification exactly?
  2. Remy Lebeau

    Binary data in String?

    That is the UTF-8 (not ANSI) encoded form of Unicode codepoint U+0081, which is a non-visual control character.
  3. Remy Lebeau

    Binary data in String?

    https://en.wikipedia.org/wiki/ISO/IEC_8859-1 there are 65 undefined characters
  4. Remy Lebeau

    Binary data in String?

    Many ANSI charsets DON'T have all 256 bytes mapped.
  5. Remy Lebeau

    TIDHTTPServer not receiving POST

    Then, the ONLY possible way that the OnCommand... event would not be called in between the OnParseAuthentication event and a subsequent OnHeadersAvailable event, as your earlier log suggested, is if TIdHTTPServer.SessionState is true and an OnInvalidSession event handler sets its VContinueProcessing parameter to False. Otherwise, what you describe is physically impossible given the following code from TIdCustomHTTPServer.DoExecute(): if not HeadersCanContinue then begin // <-- OnHeadersAvailable called here Break; end; ... if not PreparePostStream then begin // <-- OnCreatePostStream called here Break; end; ... ReadCookiesFromRequestHeader; ... s := LRequestInfo.RawHeaders.Values['Authorization']; {Do not Localize} if Length(s) > 0 then begin LAuthType := Fetch(s, ' '); LRequestInfo.FAuthExists := DoParseAuthentication(AContext, LAuthType, s, LRequestInfo.FAuthUsername, LRequestInfo.FAuthPassword); // <-- OnParseAuthentication called here if not LRequestInfo.FAuthExists then begin raise EIdHTTPUnsupportedAuthorisationScheme.Create( RSHTTPUnsupportedAuthorisationScheme); end; end; // Session management GetSessionFromCookie(AContext, LRequestInfo, LResponseInfo, LContinueProcessing); // <-- OnCreateSession and OnInvalidSession called here if LContinueProcessing then begin // These essentially all "retrieve" so they are all "Get"s if LRequestInfo.CommandType in [hcGET, hcPOST, hcHEAD] then begin DoCommandGet(AContext, LRequestInfo, LResponseInfo); // <-- OnCommandGet called here end else begin DoCommandOther(AContext, LRequestInfo, LResponseInfo); // <-- OnCommandOther called here end; end; As you can see, the calls to DoCommand...() are skipped ONLY if GetSessionFromCookie() sets LContinueProcessing to False: function TIdCustomHTTPServer.GetSessionFromCookie(AContext: TIdContext; AHTTPRequest: TIdHTTPRequestInfo; AHTTPResponse: TIdHTTPResponseInfo; var VContinueProcessing: Boolean): TIdHTTPSession; var LIndex: Integer; LSessionID: String; // under ARC, convert a weak reference to a strong reference before working with it LSessionList: TIdHTTPCustomSessionList; begin Result := nil; VContinueProcessing := True; // <-- if SessionState then begin LSessionList := FSessionList; LIndex := AHTTPRequest.Cookies.GetCookieIndex(SessionIDCookieName); while LIndex >= 0 do begin LSessionID := AHTTPRequest.Cookies[LIndex].Value; if Assigned(LSessionList) then begin Result := LSessionList.GetSession(LSessionID, AHTTPRequest.RemoteIP); if Assigned(Result) then begin Break; end; end; DoInvalidSession(AContext, AHTTPRequest, AHTTPResponse, VContinueProcessing, LSessionID); // <-- OnInvalidSession called here if not VContinueProcessing then begin Break; end; LIndex := AHTTPRequest.Cookies.GetCookieIndex(SessionIDCookieName, LIndex+1); end; { while } // check if a session was returned. If not and if AutoStartSession is set to // true, Create a new session if (Result = nil) and VContinueProcessing and FAutoStartSession then begin Result := CreateSession(AContext, AHTTPResponse, AHTTPrequest); end; end; AHTTPRequest.FSession := Result; AHTTPResponse.FSession := Result; end; There there is no possible way you can be receiving multiple HTTP requests on the same TCP connection, as TIdHTTPServer will close the current TCP connection after sending a response, Even if the client requests a keep-alive via the 'Connection' header, it will be ignored when TIdHTTPServer.KeepAlive is False.
  6. Remy Lebeau

    Binary data in String?

    Yes, quite lucky. Most ANSI locales use 1 byte per character, and UTF-16 uses 1 codeunit per character for most Western languages. So, you usually end up with 1 byte -> 2 bytes -> 1 byte conversion, hence why the final size was the same byte size, but may or may not be the same bytes as the original. There is more involved than just nul-padding, which typically only applies for bytes in the $00..$7F (ASCII) range. For non-ASCII characters, it is not a matter of simply padding '<HH>' to '<HH>#0', there is an actual mapping process involved. For example, if Windows-1252 were the locale used for the conversion, and byte $80 (Euro) were encountered, it would be converted to the Unicode character U+20AC, which is bytes $AC $20 in UTF-16LE, not $80 $00 like you are thinking. But yes, the individual bytes of the EXE data would mostly get doubled when converted to Unicode, and then truncated to single bytes when converted back to ANSI. But that does not necessarily mean that you will end up with the same bytes that you started with. For example, using Windows-1252 again, byte $81 (amongst a few others) would end up converted to either Unicode character U+FFFD (Replacement Character) or U+003F (ASCII '?') depending on the converter's implementation, which would thus be bytes $FD $FF or $3F $00 in UTF-16LE respectively, and then converted back to ANSI as byte $3F, which is clearly not the same as the original. If you absolutely need a charset that ensures no data loss when round-tripping bytes from ANSI to Unicode to ANSI, you can use codepage 437 for that, see Is there a code page that matches ASCII and can round trip arbitrary bytes through Unicode? The Unicode won't have the same character values as the original bytes in the ranges of $00..$1F and $7F..$FF, but the result of converting the Unicode back to codepage 437 will produce the original bytes. Nul-padding is not guaranteed, but yes, the String data inside the stream can get messed up. Do not use a TStringStream for binary data. Use TMemoryStream or TBytesStream instead.
  7. Remy Lebeau

    TIDHTTPServer not receiving POST

    This time, you have an extra "xxxxxxxxxxxxxxxxxxxx" between the Authorization and Content-Length headers. In that log, I see a 28-second gap between OnParseAuthentication and OnHeadersAvailable events. Those would have to be for separate requests, but are they on the same TCP connection (HTTP keep-alive)? You should not be able to get a new OnHeadersAvailable event without a preceding OnCommand... event if they are the same TCP connection. It would still be useful to see your ACTUAL code. That would not make sense for OnCommand..., unless your code is trying to validate the credentials and terminate the TCP connection before the OnCommand... event is called. The OnParseAuthentication event should only be used for PARSING credentials out of the request headers, not for VALIDATING the credentials. Any validation should only be done in the OnCommand... events. For the OnCreateSession event, do you have TIdHTTPServer.SessionState set to True or False? It is set to False by default. The only thing that TIdHTTPServer does in between calling the OnParseAuthentication and OnCommand..., events, but only if SessionState=True, is to extract session cookies from the request, compare them to the contents of TIdHTTPServer.SessionList calling the OnInvalidSession event for any cookies that are not in the list, and then calling the OnCreateSession event if a new session needs to be made. So, if SessionState=False, there is no possible way that OnCommand... would not be called after OnParseAuthentication exits. But if SessionState=True, the only way OnCommand... would not be called is if cookie/session handling is hanging/crashing.
  8. Remy Lebeau

    Binary data in String?

    Most likely, that code predated the shift to Unicode in Delphi 2009. No, it doesn't. It has the potential to corrupt the data. This is exactly why you SHOULD NOT put binary data into a UnicodeString. Doesn't work. It fills only 1/2 of the UnicodeString's memory with the non-textual binary (because SizeOf(WideChar) is 2, so the SetLength() is allocating twice the number of bytes as were read in), then converts the entire UnicodeString (including the unused bytes) from UTF-16 to ANSI producing complete garbage, and then writes that garbage as-is to file. So yes, the same number of bytes MAY be written as were read in (but that is not guaranteed), but those bytes are useless. That code is copying the binary as-is into an AnsiString of equal byte size, converting that AnsiString to a UTF-16 UnicodeString using the user's default locale, then converting that UnicodeString from UTF-16 back to ANSI using the same locale. Depending on the particular locale used, that MAY be a lossy conversion, you MIGHT end up with the same bytes that you started with, or you MIGHT NOT. This has nothing to do with pointers. You are simply performing 2 separate data conversions (binary/ANSI -> UTF-16 -> binary/ANSI ) that just HAPPEN to produce the same results as the input IN YOUR ENVIRONMENT.
  9. Remy Lebeau

    TIDHTTPServer not receiving POST

    Then there is no reason why TIdHTTPServer should not be calling the events. So, you are just going to have to debug into the server's code to find out where it is hanging. Are the OnHeadersAvailable, OnCreatePostStream, OnParseAuthentication, and OnCreateSession events being called? In the log you showed, there is an extra > before the Content-Length header. Is that really present in the request, or is that just a typo when posting the log here? Then I strongly suggest you add them.
  10. Remy Lebeau

    TIDHTTPServer not receiving POST

    Clearly the server IS receiving the POST request, you can see it in your log file. But if the OnCommand... events are not being called, it usually means the request is incomplete, ie if TIdHTTPServer is waiting for more data that is not received. Note the log also shows the TCP connection being disconnected at the same time the request is received. So, the client is likely dropping the connection on its end before sending the full 1523 bytes that it claimed to be sending. BTW, 10.6.1 is not the latest version of Indy, the current version is 10.6.2.
  11. I second VMWare, but I've always just used the free version, never the paid version.
  12. Remy Lebeau

    Error parsing winmail.dat

    Type 72 is PT_CLSID, not sure what attribute 32768 ($8000) is. TIdCoderTNEF does not implement either one at this time. There are a LOT of MAPI properties that are not implemented yet. TIdCoderTnef is mainly concerned with extracting things that make sense to apply to a TIdMessage. I can look into maybe having TIdCoderTnef just skip unknown fields instead of raising an exception on them. Or maybe exposing an event so user code can decide what to do with them.
  13. Remy Lebeau

    Is there any way to install delphi 10.4 package into Delphi 10.3?

    Package binaries are specific to each compiler version. 10.3 can only use packages that have been compiled for 10.3, etc. If you have the source code for the 10.4 package, you will have to compile it using 10.3's compiler.
  14. Remy Lebeau

    Outlook Server Execution Failed

    What does this have to do with Indy? You are getting an ActiveX/COM error, not a socket error. Either Outlook is not installed, or it is malfunctioning, or your app simply doesn't have permission to run it.
  15. Remy Lebeau

    Do bug fix patches really require active subscription?

  16. Remy Lebeau

    Find exception location from MAP file?

    Last time I tried that (last year), I couldn't get it to work. So I had to write my own stack tracing code from scratch, which "works" but it doesn't display function names yet, only memory addresses relative to the process' base address. And that project has since been EOL'ed, so I can't go back and update it.
  17. Remy Lebeau

    Prevent Alt or Ctrl + Print Screen

    On Windows 7 and later, you can use SetWindowDisplayAffinity() to specify that you want your Form window to only appear on a monitor display. That will omit (black) it out in screen captures, etc.
  18. Remy Lebeau

    Show a FMX form inside a dll from a VCL application

    Not safely, no. Is it legal to have a cross-process parent/child or owner/owned window relationship?
  19. Remy Lebeau

    Find exception location from MAP file?

    The *preferred* base addresses are in the map file, as well as in exe/dll PE headers, but it is not guaranteed that exe/dll modules will actually use those base addresses at runtime (typically, they will, but the OS is free to move them somewhere else). You can use the Win32 GetModuleHandle() function, or the RTL's SysInti.HInstance, to get the actual base address at runtime.
  20. There are 3rd party Object Inspectors available, if you search around. Or, you could write your own editor using the VCL's TValueListEditor component and some manual RTTI logic.
  21. That article is written for saving a single component to a file, not for saving multiple components. I would suggest saving the TForm itself rather than each of its child components individually.
  22. Why? The C code clearly names the parameter "num_elements", so it stands to reason that it expects an "element count", not a "byte count". A dynamic array is already a pointer, so you don't need to index into the array, PInteger(data) will suffice.
  23. Remy Lebeau

    Choose a Folder dialog

    No. IFDEF your code to call the relevant OS APIs directly (SHBrowseForFolder()/IFileOpenDialog on Windows, etc). Or else just make your own dialog/Form that iterates the filesystem (ie, via the System.IOUtils.TDirectory class) and display the contents as needed.
  24. No, it is worse than that. TThread.Synchronize() puts the specified method pointer into a global thread-safe queue, along with a waitable event, then posts a signal to the main UI thread letting it know the queue is pending, and then waits on the queued event to be signaled. The main UI thread periodically iterates the queue (whenever it receives the posted signal, as well as whenever the main message loop enters an idle state, or user code calls Classes.CheckSynchronize() directly). All of the queued methods are called in order, and their events are signaled to unblock their waiting threads.
  25. Remy Lebeau

    TFileStream fmShare modes

    Sharing flags are used with the Mode parameter, not the Rights parameter. Creation and Sharing flags can be OR'ed together in the Mode parameter, eg: Result := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyNone{, 0}); That was true in the old days when fmCreate was defined as $FFFF and thus could not be mixed with any other flags, but that is no longer the case. When the Rights parameter was introduced (which is not used on Windows when fmCreate is used), fmCreate was redefined as $FF00 so it can now be mixed with sharing flags, eg: fs := TFileStream.Create(FileName, fmCreate or fmShareDenyNone{, 0}); If no sharing mode is specified, fmShareCompat is the default, except in the specific case of the old fmCreate value ($FFFF) being used, in which case the default is fmShareExclusive instead for backwards compatibility. But either way, on Windows, fmShareCompat and fmShareExclusive mean the same thing - there is no sharing enabled on the file.