-
Content Count
2684 -
Joined
-
Last visited
-
Days Won
113
Everything posted by Remy Lebeau
-
Listen to UDP in a TThread (Windows Service)
Remy Lebeau replied to Clément's topic in ICS - Internet Component Suite
Your message loop is not dispatching any system messages it receives. For example, asynchronous socket messages (since ICS uses non-blocking sockets). You need to call DispatchMessage() for any messages that you do not process on your own, eg: while GetMessage(lMsg,0,0,0) do begin if lMsg.message = _UM_Discover+fBaseMessage then begin if lMsg.lParam = _UP_DISCOVER_PARAM_ServerSettings then begin fdhsDiscover.FindServer; end; end else begin TranslateMessage(lMsg); DispatchMessage(lMsg); end; end; -
UDP sending and receiving
Remy Lebeau replied to TurboMagic's topic in ICS - Internet Component Suite
You don't need to Listen() on a UDP socket. Only Bind() it, and then ReceiveFrom() on it. Listen() is only used for TCP. You can't Bind() to a broadcast IP, only to a local IP. -
UDP sending and receiving
Remy Lebeau replied to TurboMagic's topic in ICS - Internet Component Suite
Connect()'ing a UDP socket assigns the specified peer IP/port as a static association, so that subsequent sends always go only to that peer, and only packets from that peer can be received. It also enabled better error reporting. If a send fails with an ICMP error, such as host/port unreachable, a subsequent send/read can fail with an appropriate error code. On a Connect'ed socket, you can use Send() and Receive(), you are not restricted to SendTo() and ReceiveFrom(). -
Yes, each IDE would use its own default output folders within the VM. Source code is relatively small, so size was not an issue. Besides, I typically used fixed-sized VMs anyway. But no, storing the sources on the host did not affect the VM size. No. For me, being able to compile the same sources in multiple IDEs was important. If I changed something, I didn't want to have to copy the changes to multiple VMs to test it, just change it in one place only and then recompile as needed.
-
In my case, I used to have one IDE installed on the host itself, and then a bunch of other IDEs installed in VMs, so I kept my source files on the host and used shared folders for the VMs to access the files. Worked fine.
-
I would probably just disable the default application manifest altogether in all of the projects, and point them all to an external manifest that they can share. Then changes are needed in only one place.
-
For anyone who still visits the BCBJ website/forum, it appears to be down the past couple of days. The journal itself shutdown in 2016. The domains are not set to expire yet. I wonder if this is just a technical outage, or if this is BCBJ's finale? I have tried contacting the owner, but my emails are coming back as undeliverable.
-
One reason this is allowed is to allow debugging IDE plugins/packages at runtime. You can have a plugin/package binary loaded in one instance of the IDE while another instance of the IDE has the plugin/package's project loaded and is debugging the plugin/package while it is running in the 1st instance.
-
When the OnCreateSession event is called, the VHTTPSession parameter is initially nil. Users of the OnCreateSession event are expected to CREATE a new TIdHTTPSession object (hence the name of the event!) and assign it to the parameter before exiting (that is why the parameter is a 'var'), if they want to, ie when using a TIdHTTPSession-derived class to track custom data. After the event exits, if the parameter is still nil then TIdHTTPServer calls LSessionList.CreateUniqueSession() to create a default session object. Since you are accessing an invalid object, you should be getting an Access Violation exception that aborts the current request, sends a 500 response to the client, and calls the OnCommandError event. Sorry, I missed that when you posted your code earlier.
-
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?
-
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
That is the UTF-8 (not ANSI) encoded form of Unicode codepoint U+0081, which is a non-visual control character. -
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
https://en.wikipedia.org/wiki/ISO/IEC_8859-1 there are 65 undefined characters -
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
Many ANSI charsets DON'T have all 256 bytes mapped. -
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.
-
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
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. -
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.
-
Binary data in String?
Remy Lebeau replied to aehimself's topic in Algorithms, Data Structures and Class Design
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. -
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.
-
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.
-
virtualization VMWare Workstation PRO vs MS Hyper-V
Remy Lebeau replied to Drewsky's topic in Delphi IDE and APIs
I second VMWare, but I've always just used the free version, never the paid version. -
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.
-
Is there any way to install delphi 10.4 package into Delphi 10.3?
Remy Lebeau replied to HalfBlindCoder's topic in VCL
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. -
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.
-
Do bug fix patches really require active subscription?
Remy Lebeau replied to David Heffernan's topic in General Help
https://www.embarcadero.com/update-subscription -
Find exception location from MAP file?
Remy Lebeau replied to A.M. Hoornweg's topic in RTL and Delphi Object Pascal
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.