<rss version="2.0">
  <channel>
    <title>Rick Strahl's FoxPro Weblog</title>
    <link>https://webconnection.west-wind.com/blog/</link>
    <image>
      <url>ImageUrl</url>
      <title>Rick Strahl's FoxPro Weblog</title>
      <link>https://webconnection.west-wind.com/blog/</link>
    </image>
    <description>Wind, waves, code and everything in between</description>
    <copyright>(c) West Wind Technologies 2006-2026</copyright>
    <pubDate>2026-05-26T17:49:47.3315931Z</pubDate>
    <lastBuildDate>2026-05-25T17:59:15.8662041Z</lastBuildDate>
    <generator>Rick Strahl's West Wind Weblog</generator>
    <item>
      <title>Web Connection 8.5 Release Post</title>
      <description>&lt;p&gt;&lt;img src="https://webconnection.west-wind.com/blog/imageContent/2026/Web-Connection-8-5-Release-Post/WebConnection8.5ReleaseBanner.jpg" alt="Web Connection8.5 Release Banner"&gt;&lt;/p&gt;
&lt;p&gt;I've released Web Connection 8.5 which is a minor maintenance release of the FoxPro Web and Service Development framework. It's been a while since the last release, so there are quite a few items on the update list, but most of them fairly minor and have no impact on existing behavior.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h5 id="--one-potential-breaking-change"&gt;&lt;i class="fas fa-warning" style="font-size: 1.1em"&gt;&lt;/i&gt;  One Potential Breaking Change&lt;/h5&gt;
&lt;p&gt;There's is one potentially breaking change related to how default passwords and authentication are handled in &lt;code&gt;wwProcess&lt;/code&gt; and &lt;code&gt;wwUserSecurity&lt;/code&gt; Authentication. Specifically, this update by default enforces case sensitivity in passwords, which previously it was not. More info below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="changes"&gt;Changes&lt;/h2&gt;
&lt;p&gt;Here are the updated changes in Web Connection 8.5 broken out into some additional detail. As always you can look at the quick overview in the Web Connection Changelog:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webconnection.west-wind.com/docs/West-Wind-Web-Connection/Whats-new-in-Web-Connection.html"&gt;Web Connection Changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="server-framework-changes"&gt;Server Framework Changes&lt;/h2&gt;
&lt;h3 id="update-new-project-template-for-cors-domain-logic"&gt;Update New Project Template for CORS Domain Logic&lt;/h3&gt;
&lt;p&gt;CORS rules have gotten more strict in browsers recently, with certain browsers disallowing the catch-all &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt; rule. Previous versions of Web Connection used the &lt;code&gt;*&lt;/code&gt; value by default to allow all traffic through by default and left customization to you.&lt;/p&gt;
&lt;p&gt;CORS is used by client side Web browser applications typically using JavaScript that use the browser's Http client via the &lt;code&gt;fetch()&lt;/code&gt; function or the old &lt;code&gt;XmlHttpRequest&lt;/code&gt; object.
The problem with  &lt;code&gt;*&lt;/code&gt; is that &lt;strong&gt;recent changes&lt;/strong&gt; in Web Browser security restrictions do not allow this value longer as a catch-all value. Using that header breaks with an Http &lt;code&gt;401 Unauthorized&lt;/code&gt; result in the browser.&lt;/p&gt;
&lt;p&gt;The updated CORS configuration fixes this by explicitly specifying the caller's domain, which has the same effect but requires a little bit of extra code.&lt;/p&gt;
&lt;p&gt;Web Connection creates a default CORS implementation as part of the &lt;a href="https://webconnection.west-wind.com/docs/Walk-Through-Tutorials/Step-by-Step-Creating-a-JSON-REST-Service.html"&gt;New Project&lt;/a&gt; Wizard, which is now updated to look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;*** in YourProcess::OnProcessInit()
lcOrigin = Request.GetOrigin()

IF !EMPTY(lcOrigin) 
***    *** Optional - validate origin against a domain list (you implement)
***    IF !THIS.CheckForValidOrigin(lcOrigin)
***       Response.Status = &amp;quot;403 Forbidden&amp;quot;
***       RETURN .F.
***    ENDIF 

	*** Allow all domains IP addresses effectively 
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Origin&amp;quot;, lcOrigin)  
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Methods&amp;quot;,&amp;quot;POST, GET, DELETE, PUT, OPTIONS&amp;quot;)	
	
	lcRequestHeaders = Request.GetExtraHeader(&amp;quot;Access-Control-Request-Headers&amp;quot;)
	if EMPTY(lcRequestHeaders)
	  lcRequestHeaders = &amp;quot;Content-Type, Authorization&amp;quot; &amp;amp;&amp;amp; ,Cookie if you use cookie auth!
	endif
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Headers&amp;quot;, lcRequestHeaders)
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Credentials&amp;quot;,&amp;quot;true&amp;quot;)
ENDIF

lcVerb = Request.GetHttpVerb()
IF (lcVerb == &amp;quot;OPTIONS&amp;quot;)
    Response.Status = &amp;quot;204 No Content&amp;quot;
	RETURN .F.  &amp;amp;&amp;amp; Stop Processing
ENDIF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that rather than:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;Response.AppendHeader(&amp;quot;Access-Control-Allow-Origin&amp;quot;, &amp;quot;*&amp;quot;)  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the code now uses the original user's origin domain:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;Response.AppendHeader(&amp;quot;Access-Control-Allow-Origin&amp;quot;, lcOrigin)  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There's also a new &lt;code&gt;Request.GetOrigin()&lt;/code&gt; method that makes it easier to retrieve &lt;code&gt;HTTP_ORIGIN&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;There's also an updated topic in the documentation that describes the CORS setup for those of you that need to add or update it in existing applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webconnection.west-wind.com/docs/Walk-Through-Tutorials/Step-by-Step-Creating-a-JSON-REST-Service/Step-7-Adding-CORS-support.html"&gt;Adding CORS Support to a REST Service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="update-wwprocess-error-handling-to-catch-nested-errors-in-error-handling"&gt;Update wwProcess Error Handling to catch nested Errors in Error Handling&lt;/h3&gt;
&lt;p&gt;One long-standing issue in Web Connection has been nested error handling. Specifically the scenario where an error occurs during error handling could result in an empty response being sent back to the client from the server. The failure also would fail and not fire the rest of the error pipeline, often resulting in really hard to track down errors.&lt;/p&gt;
&lt;p&gt;In this release I updated the Web Connection process error handler to detect and better handle errors that occur during error handling. If your code fails during error handling (like say a database error during error logging) that second error is now captured and logged and a generic error page is displayed. This fixes the scenario where you get a blank page result, due to  these nested errors which were difficult to debug or pinpoint.&lt;/p&gt;
&lt;p&gt;In essence the default error handle is now wrapped in a secondary try/catch/finally loop so that any response through the default error handler will now always produce some sort of output.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h4 id="--youre-on-your-own-if-you-override-wwprocessonerror"&gt;&lt;i class="fas fa-warning" style="font-size: 1.1em"&gt;&lt;/i&gt;  You're on your Own if you override wwProcess::OnError&lt;/h4&gt;
&lt;p&gt;Last resort error handling runs through &lt;code&gt;wwProcess::OnError&lt;/code&gt; which can - and frankly should be - overriden in applications. If you override you become responsible for the error returns so make sure you follow the base class' structure for exception handling.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="wwrestprocessonjsonerror-handler"&gt;&lt;a href="https://webconnection.west-wind.com/docs/Framework-Classes/Class-wwRestProcess/wwRestProcessOnJsonError.html"&gt;wwRestProcess::OnJsonError Handler&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On a related note REST services now get a new &lt;code&gt;OnJsonError&lt;/code&gt; method which allows you to intercept REST service errors so you can easily log failures.&lt;/p&gt;
&lt;p&gt;Unlike the page &lt;code&gt;OnError()&lt;/code&gt; handler, this handler method doesn't generate any output - it only provides a mechanism for capturing the error and potentially logging it. If you want to look at generated output, and based on the headers do error management of some sort you can do so in the &lt;a href="https://webconnection.west-wind.com/docs/Framework-Classes/Class-wwRestProcess/wwRestProcessOnAfterCallMethod.html"&gt;wwRestProcess::OnAfterCallMethod()&lt;/a&gt; which provides you the generated output as input.&lt;/p&gt;
&lt;h3 id="removed-case-insensitivity-from-wwusersecurity-password-checking"&gt;Removed Case Insensitivity from wwUserSecurity Password Checking&lt;/h3&gt;
&lt;p&gt;So here's the potentially &lt;strong&gt;Breaking Change&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;I changed the behavior of base authentication in default &lt;code&gt;wwUserSecurity::Authenticate()&lt;/code&gt; - which also affects &lt;code&gt;wwProcess::OnAuthenticateUser()&lt;/code&gt; - by removing the &lt;code&gt;LOWER()&lt;/code&gt; case conversion on the input password to be lower case.&lt;/p&gt;
&lt;p&gt;The reason for this is is that modern security often requires and generates mixed case passwords and forcing case insensitivity lowers the password's strength. This affects the password behavior of the built-in wwUserSecurity security process in Web Connection, which you can override with your own &lt;code&gt;wwUserSecurity.Authenticate()&lt;/code&gt; method as you see fit.&lt;/p&gt;
&lt;p&gt;However, the default implementation is now case sensitive which &lt;strong&gt;might&lt;/strong&gt; break existing users who were expecting to use non-case sensitive passwords. Web Connection's default implementation didn't provide a mechanism to enter passwords natively other than direct entering into the DB, so how passwords are stored was always up to you. If you need to maintain password lower casing there are a few ways to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Force all Passwords to lower&lt;/strong&gt;&lt;br&gt;
You can force all passwords in the DB to lower case and then force the user's input to lower case. This is a quick fix if you don't want to do any other code changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subclass wwUserSecurity::Authenticate()&lt;/strong&gt;&lt;br&gt;
You can always subclass the &lt;code&gt;wwUserSecurity&lt;/code&gt; class and use a customized version that does the lookup in &lt;code&gt;Authenticate()&lt;/code&gt; exactly as you want it to. The default implementation has now removed the lower casing match for the password when doing the lookup but your version can put that lower casing right back in.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="client-framework-changes"&gt;Client Framework Changes&lt;/h2&gt;
&lt;h3 id="explicit-wwdotnetbridge-getfield-and-setfield-methods"&gt;Explicit wwDotnetBridge &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/Class-wwDotnetBridge/wwDotNetBridge-GetField.html"&gt;GetField()&lt;/a&gt; and &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/Class-wwDotnetBridge/wwDotNetBridge-SetField.html"&gt;SetField()&lt;/a&gt; Methods&lt;/h3&gt;
&lt;p&gt;Several people ran into issues with wwDotnetBridge due to a  (not so) recent change that removed the ability to for &lt;code&gt;GetProperty()&lt;/code&gt; and &lt;code&gt;SetProperty()&lt;/code&gt; to also access fields. Although it's really not recommended to use fields instead of Properties for public and external access, some people can't resist 😄&lt;/p&gt;
&lt;p&gt;So due to those recent changes, there are now dedicated &lt;code&gt;GetField()&lt;/code&gt; and &lt;code&gt;SetField()&lt;/code&gt; methods to that explicitly allow you to retrieve field values including private fields.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The original reasoning for field removal from the Property methods was to improve Reflection performance on fewer members to parse.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="wwsqlcdefaultlocalservername---configurable-server-when-not-provided"&gt;&lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/Class-wwSQL/wwSqlcDefaultLocalServerName.html"&gt;wwSQL::cDefaultLocalServerName - Configurable &lt;code&gt;Server=&lt;/code&gt; when not provided&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I recently ran into an issue with a local SQL Server installation that did not - for some reason - work with TCP/IP connections. A botched install that didn't have access to the SQL configuration didn't allow access via TCP/IP and no way to set it. As an aside &lt;a href="https://weblog.west-wind.com/posts/2024/Oct/24/Using-Sql-Server-on-Windows-ARM"&gt;Windows ARM devices may also not work TCP/IP with local SQL server installations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Long story short because TCP/IP failed the default local server name of &lt;code&gt;server=.&lt;/code&gt; that wwSQL uses didn't work.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wwSql&lt;/code&gt; automatically injects the &lt;code&gt;server=&lt;/code&gt; into the connection string if it's missing and the value that is injected there is now configurable via two separate values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wwSql::cDefaultLocalServerName&lt;/code&gt; Property&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WSQL_DEFAULT_LOCALSERVERNAME&lt;/code&gt; default Header Value&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The default header value in &lt;code&gt;wconnect.h&lt;/code&gt; defaults to &lt;code&gt;127.0.0.1&lt;/code&gt; and is assigned to &lt;code&gt;cDefaultLocalServerName&lt;/code&gt; as the default value. The value can be overriden per wwSQL instance or globally in one place.&lt;/p&gt;
&lt;p&gt;Again this applies only if a connection string is used that doesn't include a server name like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;loSql.Connect(&amp;quot;database=Weblog;integrated security=true; encrypt=false&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This injects the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;IF ATC(&amp;quot;server=&amp;quot;,lcConnectString) = 0
	lcConnectString = &amp;quot;server=&amp;quot; + this.cDefaultLocalServerName + &amp;quot;;&amp;quot; + lcConnectString
ENDIF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;*-- The default local server name used when omitted in connection string
cDefaultLocalServerName = WWSQL_DEFAULT_LOCALSERVERNAME
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Effectively this allows you to set specific protocols like &lt;code&gt;lpc:.&lt;/code&gt; or &lt;code&gt;np:127.0.0.1&lt;/code&gt; or &lt;code&gt;tcp:127.0.0.1,1434&lt;/code&gt; when no explicit connection string is passed. The &lt;code&gt;wconnect.h&lt;/code&gt; value allows for a global change in one place without having to assign the value to each instance.&lt;/p&gt;
&lt;p&gt;In the end this change allowed us to make a single change in the &lt;code&gt;wconnect.h&lt;/code&gt; header file to get the server to use the &lt;code&gt;lpc:127.0.0.1&lt;/code&gt; connection string, without having to upend the entire application. It's not a common feature, but it is useful in some edge case scenarios like this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ideally you should &lt;strong&gt;always&lt;/strong&gt; parameterize your connection strings. In a configuration file (create and use &lt;code&gt;Server.oConfig.cAppConnectionString&lt;/code&gt; or similar) or Environment variables or whatever. Never hardcode connection strings or any potentially configurable value if at all possible. Thank me later! 😄&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="wwftpclientaddcertificatefromcertificatestore-and--wwftpclientaddcertificate"&gt;&lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwFtpClient/AddCertificateFromCertificateStore.html"&gt;wwFtpClient::AddCertificateFromCertificateStore()&lt;/a&gt; and  &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwFtpClient/wwFtpClient-AddCertificate.html"&gt;wwFtpClient.AddCertificate()&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Added a new methods that can be used to add a certificate from the certificate store or from &lt;code&gt;.pfx&lt;/code&gt; files to the connection.&lt;/p&gt;
&lt;p&gt;For the &lt;code&gt;.AddCertificateFromCertificateStore()&lt;/code&gt; version the certificate has to be installed in the appropriate user or machine certificate store. By default the User Store is used, unless you pass the &lt;code&gt;llUseMachineName&lt;/code&gt; option as &lt;code&gt;.T.&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remember that for Web applications, the user account is the impersonated account so in Web apps for Web apps that need to add certificates from the store you probably need to install them into the local machine store.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Store Certificates are selected by their subject which look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-text"&gt;CN=example.com, O=Example Corp, OU=IT Department, L=Seattle, S=Washington, C=U
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pass the whole subject or &lt;code&gt;CN=&lt;/code&gt; value with the &lt;code&gt;CN=&lt;/code&gt; prefix (ie. &lt;code&gt;CN=example.com&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;.AddCertificate()&lt;/code&gt; you need to provide a &lt;code&gt;.pfx&lt;/code&gt; file. Pfx is a Windows specific format of the &lt;code&gt;PKCS #12&lt;/code&gt; encryption which is produced by most Windows based tools that spit out self-signed certificates (like IIS or the local certificate manager).&lt;/p&gt;
&lt;p&gt;Both of the methods that add certificates have to be called before sending any commands over the &lt;code&gt;wwFtpClient&lt;/code&gt; connection.&lt;/p&gt;
&lt;h3 id="fix-wwsftpclientuploadfile-and-wwsftpclientdownloadfile-now-preserve-file-time"&gt;Fix: &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwFtpClient/wwFtpClient-UploadFile.html"&gt;wwSFtpClient::UploadFile&lt;/a&gt; and &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwSftpClient/wwSFtpClientDownloadFile.html"&gt;wwSftpClient::DownloadFile()&lt;/a&gt; now preserve File Time&lt;/h3&gt;
&lt;p&gt;These methods now preserve the uploaded or downloaded file's time stamp on the receiving end when the file is saved.&lt;/p&gt;
&lt;p&gt;SFTP natively is a streaming API and doesn't support automatic timestamps, but wwSftpClient now explicit calls the server API to match the client and server dates by default. If you need different dates, you can use the new &lt;code&gt;SetFileTime()&lt;/code&gt; method to explicitly change the date.&lt;/p&gt;
&lt;h3 id="wwsftpclientsetfiletime"&gt;&lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwSftpClient/wwSftpClientSetFileTime.html"&gt;wwSftpClient::SetFileTime()&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Added new method to allow setting the file time of a server file more easily.&lt;/p&gt;
&lt;p&gt;Note that &lt;code&gt;UploadFile()&lt;/code&gt; and &lt;code&gt;DownloadFile()&lt;/code&gt; now preserve file dates by default so there should be less need for this method, but it can be used if you different behavior than our new imposed default of time preservation.&lt;/p&gt;
&lt;h3 id="fix-implement-missing-wwsftpclientgetdirectory"&gt;Fix: Implement missing &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwSftpClient/wwSFtpClientGetDirectory.html"&gt;wwSftpClient::GetDirectory()&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Added missing &lt;code&gt;GetDirectory()&lt;/code&gt; method that returns the currently active SFTP directory name as a string.&lt;/p&gt;
&lt;p&gt;Note that to return the files in a directory with its directory and file details, use the &lt;code&gt;ListFiles()&lt;/code&gt; method.&lt;/p&gt;
&lt;h3 id="fix-wwhttp-upload-file-name-encoding"&gt;Fix: wwHttp Upload File Name Encoding&lt;/h3&gt;
&lt;p&gt;Fixed an old issue that caused problems with filenames that include extended characters when using &lt;code&gt;nHttpPostMode=2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In some cases files, would double UTF-8 encode the &lt;code&gt;filename=&lt;/code&gt; attribute  that the server can use to determine what the file name sent was. The old code also wouldn't preserve filename case,  so it was hard to determine expected file names on the server.&lt;/p&gt;
&lt;p&gt;This has now been fixed and file names are now properly encoded as provided either by explicit filename or by deducing from the upload filename.&lt;/p&gt;
&lt;h3 id="documentation-updates-dark-mode-added-typography-and-navigation-improvements"&gt;Documentation Updates: Dark Mode added, Typography and Navigation Improvements&lt;/h3&gt;
&lt;p&gt;There are a number of documentation updates for the Web Connection (and all other West Wind) documentation - courtesy of &lt;a href="https://documentationmonster.com"&gt;Documentation Monster&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The help file now has a toggle for light and dark mode. Not a big thing for most, but some (very vocal) people have been asking for dark most on the documentation. The Web Connection docs continue to default to light mode, but you can now toggle to dark mode on the top right  toolbar.&lt;/p&gt;
&lt;p&gt;The typography has also been updated with more readable fonts, and sizing of the main panel has been adjusted a bit for various display sizes.&lt;/p&gt;
&lt;p&gt;Finally the tree navigation now manages topic positioning better, both when jumping in to specific topic links from outside of the docs site as well as when searching in the topic tree. Tree searches should also be significantly faster now than before.&lt;/p&gt;
&lt;h3 id="web-connection-weblog-facelift"&gt;&lt;a href="https://webconnection.west-wind.com/blog"&gt;Web Connection Weblog Facelift&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I've migrated the Web Connection Weblog to my main Weblog engine that I also use and have recently updated on my &lt;a href="https://weblog.west-wind.com"&gt;main weblog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As a result the blog now has a more modern look with all old content migrated into the new engine. All old links now forward to the new blog site.&lt;/p&gt;
&lt;p&gt;As with the docs site there are improvements like dark mode, improved typography and better sizing for various mobile and desktop form factors.&lt;/p&gt;
&lt;p&gt;The old Web Connection blog site was getting a bit long in the tooth and was a bear to maintain. While the site has been working fine, it was missing important back end features necessary to maintain the blog operations.&lt;/p&gt;
&lt;p&gt;The new site has been re-homed at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://webconnectionblog.west-wind.com"&gt;https://webconnectionblog.west-wind.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;and all old links into the old site now forward to the new site.&lt;/p&gt;
&lt;p&gt;If you run into any dead links, please let me know...&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;As I mentioned in the beginning, it's been a while since the last release, so a lot of the small fixes have been building up making this is a bigger release than many of the preceding small releases.&lt;/p&gt;
&lt;p&gt;However, none of these updated features and enhancements have any significant impact on existing applications in terms of changes required, with the potential exception of the the password behavior in &lt;code&gt;wwUserSecurity.Authenticate()&lt;/code&gt;  and by association default &lt;code&gt;wwUserSecurity&lt;/code&gt; Authentication in &lt;code&gt;wwProcess&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Until next time...&lt;/p&gt;
</description>
      <link>https://webconnection.west-wind.com/blog/posts/2026/May/25/Web-Connection-85-Release-Post</link>
      <guid isPermaLink="false">vvtierno9k12</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2026/May/25/Web-Connection-85-Release-Post#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2026/May/25/Web-Connection-85-Release-Post</guid>
      <pubDate>Mon, 25 May 2026 07:59:15 GMT</pubDate>
    </item>
    <item>
      <title>What is CORS and how to set it up in West Wind Web Connection</title>
      <description>&lt;p&gt;&lt;img src="https://webconnection.west-wind.com/blog/imageContent/2026/What-is-CORS-and-how-to-set-it-up-in-West-Wind-Web-Connection/CORSBanner.jpg" alt="CORS Banner"&gt;&lt;/p&gt;
&lt;p&gt;CORS stands for &lt;strong&gt;Cross Origin Resource Sharing&lt;/strong&gt; and it's a security feature that you need to be aware of if you're building any Http based REST services that are called from a Web browser and that are accessed across multiple domains. This applies if you have a front-end Web site that runs on one domain, and a back-end server that lives on another domain (or IP address). CORS typically kicks in when making script based requests from a browser for these cross domain calls.&lt;/p&gt;
&lt;p&gt;The protocol is an odd one, in that it's typically enforced only by Web Browsers, and not in play for most other Http clients like say &lt;code&gt;wwHttp&lt;/code&gt; or &lt;code&gt;XMLHttpRequest&lt;/code&gt; in FoxPro, or &lt;code&gt;HttpClient&lt;/code&gt; in .NET unless you explicitly mimic the &lt;code&gt;Origin&lt;/code&gt; and &lt;code&gt;Access-Allow-Request&lt;/code&gt; headers that the protocol uses.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;When a browser makes a cross-origin &lt;code&gt;fetch()&lt;/code&gt; or &lt;code&gt;XMLHttpRequest&lt;/code&gt; call from script, it sends a request for CORS headers which the server should respond to with it's own set of response headers. The browser then checks the server’s response for specific CORS headers like &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; and &lt;code&gt;Access-Control-Allow-Headers&lt;/code&gt;. If the headers match the requesting origin and other headers, the browser allows the calling JavaScript to access the response. If it doesn't match the browser doesn't expose the response to the calling JavaScript Http client and throws a script error on the request.&lt;/p&gt;
&lt;p&gt;The purpose of CORS is to allow the server to tell the Web Browser whether it is allowed access to the requested Url. Although CORS has to be implemented by the server, CORS is really &lt;strong&gt;a Web Browser security feature&lt;/strong&gt; that only is enforced by Web Browsers, but not other Http clients.&lt;/p&gt;
&lt;p&gt;For example the server may want to ensure that requests only come from one or two domains (origins) that are allowed to access the service when called from a browser. Note that CORS doesn't verify or authorize requests - it's merely a protocol feature that sends requesting headers that are confirmed by the server for a follow-on or in-flight request to process.&lt;/p&gt;
&lt;h3 id="web-browser-only-protocol-but-implemented-by-the-server"&gt;Web Browser Only Protocol but implemented by the Server&lt;/h3&gt;
&lt;p&gt;It's an odd protocol because &lt;strong&gt;it's only used in Web Browsers&lt;/strong&gt;, and mostly irrelevant for other Http clients. The reason is that the actual CORS 'security' feature - rejecting a request on invalid or missing CORS data - is implemented by the Web Browser Http client. So it's up to the client to provide this logic and in general only Web Browsers implement CORS security. Other Http never send CORS request nor do they process them or restrict access based on them.&lt;/p&gt;
&lt;h3 id="cors-doesnt-fire-on-localhost"&gt;CORS doesn't fire on localhost&lt;/h3&gt;
&lt;p&gt;It's also easy to forget about CORS during development, because CORS doesn't kick in when running &lt;strong&gt;same origin&lt;/strong&gt; requests. If your Web page and REST Service run on the same domain/IP Address, there's no CORS. During development that's very common, while deployed applications often run on separate servers. Also if you're using Http testing tools like &lt;a href="Https://websurge.west-wind.com"&gt;West Wind WebSurge&lt;/a&gt; or &lt;a href="Https://www.postman.com/"&gt;Postman&lt;/a&gt;, CORS doesn't automatically kick in unless you explicitly provide the CORS request headers like &lt;code&gt;Origin&lt;/code&gt; and any &lt;code&gt;Access-Allow-Request-xxxx&lt;/code&gt; headers. Hence it's easy to forget to test CORS use cases while testing REST applications. Make a point of remembering and/or ensuring you set up specific tests in your Http client request testing suite (you are using that with your services, right? 😄)&lt;/p&gt;
&lt;h2 id="the-cors-protocol"&gt;The CORS Protocol&lt;/h2&gt;
&lt;p&gt;CORS is implemented at the Http protocol level via Http headers that are passed from client to server, and back to the client.&lt;/p&gt;
&lt;p&gt;It works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The client sends an &lt;code&gt;Origin&lt;/code&gt; header and potentially several &lt;code&gt;Access-Allow-Request-xxxx&lt;/code&gt; headers&lt;/li&gt;
&lt;li&gt;This signals that the server should return a CORS response&lt;/li&gt;
&lt;li&gt;The CORS response needs to include at minimum:
&lt;ul&gt;
&lt;li&gt;The domain that is allowed access (ie. the current domain)&lt;/li&gt;
&lt;li&gt;The Http Verbs that are allowed&lt;/li&gt;
&lt;li&gt;The Http Headers that are allowed&lt;/li&gt;
&lt;li&gt;Whether security (Authorization, Cookies) is allowed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depending what type of request you're making CORS data is either made via a separate, pre-flight Http &lt;code&gt;OPTIONS&lt;/code&gt; request, or via in-flight headers that are part of a 'normal' request flow.&lt;/p&gt;
&lt;h3 id="pre-flight-options-request"&gt;Pre-flight OPTIONS Request&lt;/h3&gt;
&lt;p&gt;For  most &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt; operations browsers send a pre-flight &lt;code&gt;OPTIONS&lt;/code&gt; request to the server which requests a CORS header response. In effect this results in &lt;strong&gt;two requests&lt;/strong&gt; being made to the server by the browser: An &lt;code&gt;OPTIONS&lt;/code&gt; request for CORS verification, followed by the original full Http request.&lt;/p&gt;
&lt;p&gt;The Http &lt;code&gt;OPTIONS&lt;/code&gt; request from client requests &lt;strong&gt;only the Http headers&lt;/strong&gt; for the request and the response returned should contain only the Http headers that a server is expected to return for &lt;strong&gt;this request&lt;/strong&gt;, which &lt;strong&gt;includes the CORS headers&lt;/strong&gt;. The response code should return headers, no data and have a result Status Code  &lt;code&gt;204 No Data&lt;/code&gt; although &lt;code&gt;200 OK&lt;/code&gt; with no data also works - the browser doesn't really care about the result code or content, only the headers.&lt;/p&gt;
&lt;p&gt;Here's what that looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://webconnection.west-wind.com/blog/imageContent/2026/What-is-CORS-and-how-to-set-it-up-in-West-Wind-Web-Connection/PreflightOptionsRequestion.png" alt="Preflight Options Requestion"&gt;&lt;br&gt;
&lt;small&gt;&lt;strong&gt;Figure 1&lt;/strong&gt; - A pre-flight OPTIONS CORS request&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Note that this is an &lt;code&gt;OPTIONS&lt;/code&gt; request that was originally triggered by a &lt;code&gt;POST&lt;/code&gt; operation that also requires Authentication in this case. Note that the client sets &lt;code&gt;Access-Control-Request&lt;/code&gt; headers to specify what operations it wants to perform which the server response needs to include by adding the corresponding &lt;code&gt;Access-Control-Allow&lt;/code&gt; headers. Remember these are typically sent by a Web browser making a cross-origin request via script code.&lt;/p&gt;
&lt;p&gt;The server has to respond with headers that include the requested origin, headers and methods. If the CORS headers aren't matched the actual request (ie. the &lt;code&gt;POST&lt;/code&gt; request in this case) is never fired and the &lt;code&gt;OPTIONS&lt;/code&gt; request fails the original POST operation that initiated this sequence at the client (ie. the &lt;code&gt;fetch()&lt;/code&gt; or &lt;code&gt;XMLHttpRequest&lt;/code&gt; call).&lt;/p&gt;
&lt;p&gt;To clarify the full request flow in this example is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Browser makes a &lt;code&gt;fetch()&lt;/code&gt; request with  &lt;code&gt;POST&lt;/code&gt; and Authentication&lt;/li&gt;
&lt;li&gt;Browser fires &lt;code&gt;OPTIONS&lt;/code&gt; request&lt;/li&gt;
&lt;li&gt;Server responds with valid CORS response&lt;/li&gt;
&lt;li&gt;Browser makes the original &lt;code&gt;POST&lt;/code&gt; request&lt;/li&gt;
&lt;li&gt;Server responds to &lt;code&gt;POST&lt;/code&gt; request&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="in-flight-request"&gt;In-flight Request&lt;/h3&gt;
&lt;p&gt;For simple &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; requests that don't include authentication, cookie or custom headers, an &lt;code&gt;Origin&lt;/code&gt; header is sent without a separate explicit &lt;code&gt;OPTIONS&lt;/code&gt; request and only the single original request is sent. Instead the CORS headers are checked as part of the incoming request and your full response needs to include the CORS headers directly.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://webconnection.west-wind.com/blog/imageContent/2026/What-is-CORS-and-how-to-set-it-up-in-West-Wind-Web-Connection/InflightPostRequest.png" alt="Inflight Post Request"&gt;&lt;br&gt;
&lt;small&gt;&lt;strong&gt;Figure 2&lt;/strong&gt; - An in-flight CORS request adds CORS headers to the normal request and response headers. &lt;code&gt;Origin&lt;/code&gt; is the trigger.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Since in-flight requests don't use a separate request to determine whether the request can execute, it only includes an &lt;code&gt;Origin&lt;/code&gt; header to validate that the domain is allowed. Everything else is moot, because the request is already in process. POST operations sometimes send the &lt;code&gt;Access-Control-Request-Headers&lt;/code&gt;, but it's not required which means you can't just assume to echo back the Headers that were sent - or not add the CORS headers if you decide to not reject the CORS request.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3 id="cors-failure-and-success-responses"&gt;CORS Failure and Success Responses&lt;/h3&gt;
&lt;p&gt;If the client makes a CORS request and your server code decides it doesn't want to allow the request to process, there are number of ways to do this.&lt;/p&gt;
&lt;p&gt;For failures simply return no content with &lt;code&gt;403 Forbidden&lt;/code&gt;, and don't add any of the CORS headers, which effectively fails the CORS request on the client. Any &lt;code&gt;fetch()&lt;/code&gt; or &lt;code&gt;XMLHttpRequest&lt;/code&gt; call will then fail in JavaScript code.&lt;/p&gt;
&lt;p&gt;For a successful &lt;code&gt;OPTIONS&lt;/code&gt; response use &lt;code&gt;204 No Content&lt;/code&gt;, make sure to return the CORS response headers and then exit &lt;strong&gt;before processing the rest of the request&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For in-flight non-OPTIONS responses, make sure to add the CORS response headers, and continue processing your request as normal. No need to adjust any special Http Response code.&lt;/p&gt;
&lt;blockquote&gt;
&lt;h5 id="--beware-of-access-control-allow-origin-"&gt;&lt;i class="fas fa-warning" style="font-size: 1.1em"&gt;&lt;/i&gt;  Beware of Access-Control-Allow-Origin: *&lt;/h5&gt;
&lt;p&gt;In previous versions of Web Connection (and other server frameworks for that matter) the default generated CORS response for 'all origins allowed'  was to specify &lt;code&gt;*&lt;/code&gt; for the origin value. Although this is a valid value per spec, in recent years several browsers - namely Safari on Mac and iOS - are refusing to accept any * wildcard values for any of the CORS response headers.&lt;/p&gt;
&lt;p&gt;For this reason as of Web Connection 8.5 the templates have changed from the old &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt; syntax to explicitly retrieving the &lt;code&gt;Origin&lt;/code&gt; header and echoing it back in the outgoing header value. The code at the end of this article shows the new approach that works in this more restricted environment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="cors-in-web-connection"&gt;CORS in Web Connection&lt;/h2&gt;
&lt;p&gt;Web Connection doesn't have direct support for CORS as part of the low level Web Connection Web Server connectors, so CORS support has to be implemented at the application level. It's an optional feature and typically only needed for REST projects, although in  some rare situations you may also need it if you have individual REST requests as part of an HTML application (rare, but it happens).&lt;/p&gt;
&lt;p&gt;When you create a new REST project via the &lt;strong&gt;New Project Wizard&lt;/strong&gt; Web Connection automatically adds CORS support into the generated Process class in &lt;code&gt;OnProcessInit()&lt;/code&gt;. The code checks for an &lt;code&gt;Origin&lt;/code&gt; header and then displays the appropriate CORS response headers, based on the incoming request. This ensures that valid cross-origin requests are properly acknowledged and allowed by the browser.&lt;/p&gt;
&lt;p&gt;The semi generic code to do this lives in your Process class in &lt;code&gt;OnProcessInit()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;FUNCTION OnProcessInit
LOCAL lcOrigin, lcRequestHeaders, lcVerb

*** Unrelated
Response.Encoding = &amp;quot;UTF8&amp;quot;
Request.lUtf8Encoding = .T.

*** Add CORS headers to allow cross-site access on REST calls for browser access
lcOrigin = Request.ServerVariables(&amp;quot;Http_ORIGIN&amp;quot;)

IF !EMPTY(lcOrigin) 
	*** Allow all domains IP addresses effectively
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Origin&amp;quot;, lcOrigin)  
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Methods&amp;quot;,&amp;quot;POST, GET, DELETE, PUT, OPTIONS&amp;quot;)	
	
	lcRequestHeaders = Request.GetExtraHeader(&amp;quot;Access-Control-Request-Headers&amp;quot;)
	if EMPTY(lcRequestHeaders)
	  lcRequestHeaders = &amp;quot;Content-Type, Authorization&amp;quot; &amp;amp;&amp;amp; ,Cookie if you use cookie auth!
	endif
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Headers&amp;quot;, lcRequestHeaders)
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Credentials&amp;quot;,&amp;quot;true&amp;quot;)
ENDIF

lcVerb = Request.GetHttpVerb()
IF (lcVerb == &amp;quot;OPTIONS&amp;quot;)
    Response.Status = &amp;quot;204 No Content&amp;quot;
	RETURN .F.  &amp;amp;&amp;amp; Stop Processing
ENDIF

*** ... other OnProcessInit() code
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code first checks for the &lt;code&gt;Origin&lt;/code&gt; client header, which determines whether the request is cross-site and requires the server to return a CORS response. No &lt;code&gt;Origin&lt;/code&gt; means no CORS data is required on the request. So local domain access, or access from a non-Web Browser request won't trigger CORS requests.&lt;/p&gt;
&lt;p&gt;When a CORS request comes in here are the items required:&lt;/p&gt;
&lt;h3 id="original-origins"&gt;Original Origins&lt;/h3&gt;
&lt;p&gt;Next this code implements the default &lt;code&gt;Origin&lt;/code&gt; behavior which returns the origin requested, which &lt;strong&gt;effectively allows access to all domains&lt;/strong&gt; since we're always returning what the client requests.&lt;/p&gt;
&lt;p&gt;If you want to allow only certain domains, you can create some sort of lookup function or even a hard code a list of domains to allow. If a CORS request fails you can return a &lt;code&gt;403 Forbidden&lt;/code&gt; status - and not return the CORS headers.&lt;/p&gt;
&lt;p&gt;Here's what that looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;IF !EMPTY(lcOrigin) 
    IF !THIS.CheckForValidOrigin(lcOrigin)
       Response.Status = &amp;quot;403 Forbidden&amp;quot;
       RETURN .F.
    ENDIF 
    
	Response.AppendHeader(&amp;quot;Access-Control-Allow-Origin&amp;quot;, lcOrigin)  
    ...
ENDIF
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="methods"&gt;Methods&lt;/h3&gt;
&lt;p&gt;You need to specify the allowed methods when OPTIONS requests are made. In OPTIONS requests the client will send &lt;code&gt;Access-Control-Request-Methods&lt;/code&gt; which you can echo back. Not though that this value is not present in inflight requests so it's better to use a fixed set of Http Verbs that you are planning to use in your project.&lt;/p&gt;
&lt;p&gt;Easiest just to return all the methods your app supports, but you could also return the specific verb being requested.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;Response.AppendHeader(&amp;quot;Access-Control-Allow-Methods&amp;quot;,&amp;quot;POST, GET, DELETE, PUT, OPTIONS&amp;quot;)	
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that in an &lt;code&gt;OPTIONS&lt;/code&gt; command you need to include &lt;code&gt;OPTIONS&lt;/code&gt; plus &lt;code&gt;Access-Control-Request-Methods&lt;/code&gt; rather than just using &lt;code&gt;Request.GetHttpVerb()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="headers"&gt;Headers&lt;/h3&gt;
&lt;p&gt;This one is a little tricky since you may not know what headers you need to support for all requests. &lt;code&gt;OPTIONS&lt;/code&gt; requests provide an explicit &lt;code&gt;Access-Control_Request-Header&lt;/code&gt; value that you can echo back, but again there's no guarantee that this value exists so you want to make sure you have a default value ready using this logic:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-foxpro"&gt;lcRequestHeaders = Request.GetExtraHeader(&amp;quot;Access-Control-Request-Headers&amp;quot;)
IF EMPTY(lcRequestHeaders)
  lcRequestHeaders = &amp;quot;Content-Type, Authorization&amp;quot; &amp;amp;&amp;amp; ,Cookie if you use cookie auth or Sessions!
ENDIF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tricky bit here is that &lt;strong&gt;you need to provide all custom headers&lt;/strong&gt; that you might send. While that may seem easy for individual requests, it's more difficult to figure out exactly what's needed for &lt;em&gt;every request&lt;/em&gt; in an application and distill that down to a single set of headers. Hence you'll want to use the requested headers if available. If you have custom headers it'll always trigger an &lt;code&gt;OPTIONS&lt;/code&gt; request, so in that case the &lt;code&gt;Allow-Control-Request-Headers&lt;/code&gt; should be sent with the incoming request and that's what you should return in your CORS header response.&lt;/p&gt;
&lt;h3 id="allow-credentials"&gt;Allow Credentials&lt;/h3&gt;
&lt;p&gt;If your app has any authentication you'll want to set this value to true. This is needed if you have &lt;code&gt;Authorization&lt;/code&gt; or &lt;code&gt;Cookie&lt;/code&gt; headers.&lt;/p&gt;
&lt;p&gt;Kind of silly that this is required since the headers should be able to determine whether this is required.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;CORS is a clunky protocol and when you first look at it, it doesn't seem to be very effective at providing any security at all. However, for browsers it is useful in ensuring that errand Web browsers can't spoof requests from an embedded iframe for example. The server can explicitly check valid source domains and refuse to serve requests if the list of client domains is not met. However, that does not negate the problem because non-Web Browser clients can call your server any way they want - including potentially spoofed origin domains.&lt;/p&gt;
&lt;p&gt;The good news is that it's easy to implement CORS for your REST services in Web Connection. There's only a little bit of code required, and in can be placed into a single entry point in &lt;code&gt;OnProcessInit()&lt;/code&gt; in one place. If you're creating new REST projects, Web Connection automatically provides the CORS code in the generated process class and if you have existing code you can easily copy in the code from this article...&lt;/p&gt;
&lt;div style="margin-top: 30px;font-size: 0.8em;
            border-top: 1px solid #eee;padding-top: 8px;"&gt;
    &lt;img src="Https://markdownmonster.west-wind.com/favicon.png" style="height: 20px;float: left; margin-right: 10px;"&gt;
    this post created and published with the 
    &lt;a href="Https://markdownmonster.west-wind.com" target="top"&gt;Markdown Monster Editor&lt;/a&gt; 
&lt;/div&gt;
</description>
      <link>https://webconnection.west-wind.com/blog/posts/2025/Aug/04/What-is-CORS-and-how-to-set-it-up-in-West-Wind-Web-Connection</link>
      <guid isPermaLink="false">57038</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2025/Aug/04/What-is-CORS-and-how-to-set-it-up-in-West-Wind-Web-Connection#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2025/Aug/04/What-is-CORS-and-how-to-set-it-up-in-West-Wind-Web-Connection</guid>
      <pubDate>Mon, 04 Aug 2025 00:38:39 GMT</pubDate>
    </item>
    <item>
      <title>Web Connection 8.4 Release Post</title>
      <description>&lt;p&gt;&lt;img src="https://webconnection.west-wind.com/images/WebConnection_Code_Banner.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Hi all,&lt;/p&gt;
&lt;p&gt;I've released Web Connection 8.4 which is a minor maintenance release of the FoxPro Web and Service Development framework. The primary reason for this release are several small bug fixes that might be impacting some of you if you are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using wwUserSecurity Authentication (especially in Virtual Folders)&lt;/li&gt;
&lt;li&gt;You're using Multipart Form Uploads via wwHttp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If that's you and you're using v8.1-8.3, you'll probably want to update to the latest version as soon as possible.&lt;/p&gt;
&lt;p&gt;There are also a couple of nice enhancements in wwDotnetBridge, and a new documentation viewer offline application.&lt;/p&gt;
&lt;h2 id="bug-fixes"&gt;Bug Fixes&lt;/h2&gt;
&lt;p&gt;Let's start with the important bug fixes because they are the main reason for this release.&lt;/p&gt;
&lt;h3 id="fix-user-authentication-session-cookie-bug"&gt;Fix User Authentication Session Cookie Bug&lt;/h3&gt;
&lt;p&gt;In the last 8.2-8.3 a regression error was introduced that in certain situations would not properly set the wwUserSecurity related authentication Session cookie. Specifically this can be a problem in scenarios where you're using a non-root virtual folder in your Web application.&lt;/p&gt;
&lt;p&gt;I'm not quite sure why this particular error occurred only with virtual folders since Web Connection cookies are always set on the domain root, but for some reason cookies set when running under the virtual in some cases would not be set properly. The issue basically was that a session Id was present but for some reason the session was not recovered and so a new session key gets generated on each check attempt which effectively results in a successful login that lasts only for the current requests. Oddly this only occurs in virtuals and then more likely on nested virtuals (for me it happened in the &lt;a href="https://west-wind.com/wconnect/weblog"&gt;Web Connection Weblog&lt;/a&gt; specifically).&lt;/p&gt;
&lt;p&gt;It's fixed now where the code now explicitly generates a new cookie on login rather than checking for an existing cookie to update.&lt;/p&gt;
&lt;h3 id="fix-wwhttp-multipart-form-data-content-type-not-getting-set"&gt;Fix: wwHttp Multipart Form Data Content Type not getting set&lt;/h3&gt;
&lt;p&gt;This issue is another regression that cropped up from the recent work to support greater than 16mb uploads and downloads. Essentially, the Content Type parameter on &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwHTTP/wwHTTPAddPostKey.html"&gt;AddPostKey()&lt;/a&gt; was not being passed through in to the request. It does work for the newly added &lt;code&gt;AddPostFile()&lt;/code&gt; but if you're using older code that uses &lt;code&gt;AddPostKey()&lt;/code&gt; to upload files the content type was not being appended.&lt;/p&gt;
&lt;p&gt;In the process of fixing this bug, I also cleaned up the signature to the &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/West-Wind-Internet-Protocols/Class-wwHTTP/wwHTTPAddPostFile.html"&gt;AddPostFile()&lt;/a&gt; method. The method initially had inherited the same signature as &lt;code&gt;AddPostKey()&lt;/code&gt; but there were several parameters that are superfluous for uploading files, so the signature was adjusted for more logical flow.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The change to &lt;code&gt;AddPostKey&lt;/code&gt; is a potential breaking change if you were using the method since the order of parameters has changed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="missing-jsonservice-variable-in-wwrestprocess-process-methods"&gt;Missing JsonService Variable in wwRestProcess Process Methods&lt;/h3&gt;
&lt;p&gt;Another regression bug relates to &lt;a href="https://webconnection.west-wind.com/docs/Framework-Classes/Class-wwRestProcess.html"&gt;wwRestService&lt;/a&gt; and the use of the intrinsic &lt;code&gt;JsonService&lt;/code&gt; variable that is passed as a shortcut for &lt;code&gt;THIS.oJsonService&lt;/code&gt; into a REST Process method. The error was due a missing &lt;code&gt;PRIVATE&lt;/code&gt; scope assignment at the top of the processing pipeline in &lt;code&gt;RouteRequest()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This has been fixed.&lt;/p&gt;
&lt;h2 id="new-and-improved-features"&gt;New and Improved Features&lt;/h2&gt;
&lt;p&gt;Not much to report in this update but there are few.&lt;/p&gt;
&lt;h3 id="wwdotnetbridge-unblock-all-dlls-loaded"&gt;wwDotnetBridge: Unblock all DLLs loaded&lt;/h3&gt;
&lt;p&gt;The classic .NET Framework still uses Windows Vista Style zoning security by default and so respects the custom blocking attributes that are added to binary files downloaded from the Internet (including files embedded in Zip or 7zip files when unpacked), media devices or from non-local domain drives. Files from those sources get implicitly marked by Windows as 'blocked'.&lt;/p&gt;
&lt;p&gt;wwDotnetBridge has for some time explicitly removed blocks on &lt;code&gt;wwDotnetBridge.dll&lt;/code&gt; which is the initially loaded Dll that loads the runtime. In most cases that's enough, but I've recently run into cases where other DLLs being loaded were also affected and failed when calling &lt;code&gt;loBridge.LoadAssembly()&lt;/code&gt;.  For this reason, wwDotnetBridge now also removes blocks from any assembly loaded via &lt;code&gt;LoadAssembly()&lt;/code&gt; before loading.&lt;/p&gt;
&lt;p&gt;This should improve issues with blocked DLL loading significantly, but you may &lt;strong&gt;still run into problems&lt;/strong&gt; if the DLLs loaded load other DLLs implicitly. In that case you can explicitly load those assemblies using &lt;code&gt;LoadAssembly()&lt;/code&gt; (starting with the most deeply nested dependency first), or you can manually unblock files as described in this &lt;a href="https://webconnection.west-wind.com/docs/Utility-Classes/Class-wwDotnetBridge/Unable-to-load-CLR-Instance-Error.html"&gt;help topic.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="new-offline-documentation-viewer"&gt;New Offline Documentation Viewer&lt;/h3&gt;
&lt;p&gt;For years several people here - looking at you Tore - have hounded me to provide offline documentation again as we used some time ago. In the past I created a very large PDF document which was problematic to create as I used Word Automation to import from Html and then Print to Pdf. That process was very brittle and took a really long time to run and often would randomly fail.&lt;/p&gt;
&lt;p&gt;Well, recently I switched the documentation over to a new documentation system I've been building called &lt;a href="https://documentationmonster.com"&gt;Documentation Monster&lt;/a&gt;, which is a Help Builder like system built ontop of my popular &lt;a href="https://markdownmonster.west-wind.com"&gt;Markdown Monster&lt;/a&gt; editor.&lt;/p&gt;
&lt;p&gt;As part of that tool there's a new option to create a self contained documentation viewer application that can be used to browse the online documentation offline.&lt;/p&gt;
&lt;p&gt;Here's what that looks like for Web Connection:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://webconnection.west-wind.com/docs/images/WebConnectionDocumentationViewer.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;All the functionality of the Web site is available in the offline Viewer so you get all the same docs - offline.&lt;/p&gt;
&lt;p&gt;The big benefit from my end is that's easy and fast to build so this documentation is much easier to keep in sync with the online documentation.&lt;/p&gt;
&lt;p&gt;The tool produces a self contained EXE that itself is very small. The size of the EXE is determined primarily by the size of the documentation which is embedded inside of the Exe and unpacked when run. That said the Web Connection file is still in the 20mb range zipped due to the huge amount of content and media. The Exe has no dependencies as it runs on the built-in .NET framework.&lt;/p&gt;
&lt;p&gt;You can check it out from here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webconnection.west-wind.com/docs/West-Wind-Web-Connection/Offline-Documentation.html"&gt;Download the Web Connection Documentation Offline Viewer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Overall this is a very small release that's primarily been released for the bug fixes (which have been avaiable for a while in the Experimental Updates Zip file as soon as they were discovered and fixed).&lt;/p&gt;
&lt;p&gt;I would recommend updating to the latest version if you are on 8.1-8.3.&lt;/p&gt;
</description>
      <link>https://webconnection.west-wind.com/blog/posts/2025/Jul/23/Web-Connection-84-Release-Post</link>
      <guid isPermaLink="false">57037</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2025/Jul/23/Web-Connection-84-Release-Post#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2025/Jul/23/Web-Connection-84-Release-Post</guid>
      <pubDate>Wed, 23 Jul 2025 01:32:21 GMT</pubDate>
    </item>
    <item>
      <title>Creating and Debugging .NET Assemblies for wwDotnetBridge and Visual FoxPro</title>
      <description>wwDotnetBridge lets you interface with .NET code directly, but if need to access more than a handful of lines of .NET code from FoxPro, it's a good idea to create a separate dedicated .NET component and call that instead. There are many benefits to easier and more discoverable access to functionality, better performance and support for a few features that don't work well through wwDotnetBridge. In this post I show how to create a .NET component, build and run it, and how debug it as well.</description>
      <link>https://webconnection.west-wind.com/blog/posts/2025/May/23/Creating-and-Debugging-Dotnet-Assemblies-for-wwDotnetBridge-and-Visual-FoxPro</link>
      <guid isPermaLink="false">57036</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2025/May/23/Creating-and-Debugging-Dotnet-Assemblies-for-wwDotnetBridge-and-Visual-FoxPro#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2025/May/23/Creating-and-Debugging-Dotnet-Assemblies-for-wwDotnetBridge-and-Visual-FoxPro</guid>
      <pubDate>Fri, 23 May 2025 03:49:41 GMT</pubDate>
    </item>
    <item>
      <title>FoxPro Running on a Windows ARM Device</title>
      <description>I recently picked up a Windows ARM 'Co-Pilot' capable laptop and took it for a spin running my typical spread of Windows applications and tools. I also checked out how well it works with FoxPro - here's what I found.</description>
      <link>https://webconnection.west-wind.com/blog/posts/2024/Nov/07/FoxPro-Running-on-a-Windows-ARM-Device</link>
      <guid isPermaLink="false">57035</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2024/Nov/07/FoxPro-Running-on-a-Windows-ARM-Device#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2024/Nov/07/FoxPro-Running-on-a-Windows-ARM-Device</guid>
      <pubDate>Thu, 07 Nov 2024 06:42:14 GMT</pubDate>
    </item>
    <item>
      <title>Web Connection 8.1 Release Post</title>
      <description>Web Connection 8.1 is out. This is a small maintenance release, but it does include a few significant updates.</description>
      <link>https://webconnection.west-wind.com/blog/posts/2024/Oct/14/Web-Connection-81-Release-Post</link>
      <guid isPermaLink="false">57034</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2024/Oct/14/Web-Connection-81-Release-Post#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2024/Oct/14/Web-Connection-81-Release-Post</guid>
      <pubDate>Mon, 14 Oct 2024 19:29:02 GMT</pubDate>
    </item>
    <item>
      <title>Making Web Connection Work with Response Output Greater than 16mb</title>
      <description>Web Connection in the past has not supported &gt; 16mb direct output via plain string based output, due to FoxPro's 16mb string limit. 16mb is a lot of text and while I generally don't recommend returning that much data as part of non-file request (which does support larger files) it's a feature request that comes up from time to time as people overrun the limit. During last weekend's SW Fox conference I heard about this issue again in a session and decided to address it once and for all and this posts describes the change and how it's implemented.</description>
      <link>https://webconnection.west-wind.com/blog/posts/2024/Sep/30/Making-Web-Connection-Work-with-Response-Output-Greater-than-16mb</link>
      <guid isPermaLink="false">57033</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2024/Sep/30/Making-Web-Connection-Work-with-Response-Output-Greater-than-16mb#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2024/Sep/30/Making-Web-Connection-Work-with-Response-Output-Greater-than-16mb</guid>
      <pubDate>Mon, 30 Sep 2024 22:30:22 GMT</pubDate>
    </item>
    <item>
      <title>wwDotnetBridge Revisited: An updated look at FoxPro .NET Interop</title>
      <description>In this very long white paper for the Southwest Fox conference, I discuss the basics of wwDotnetBridge and then demonstrate a variety of functionality with 10 examples. We'll see basic usage, how to wrap classes in FoxPro and .NET, how to use a variety of .NET 3rd party libraries, how to handle .NET events and how to make Task based async calls.</description>
      <link>https://webconnection.west-wind.com/blog/posts/2024/Sep/23/wwDotnetBridge-Revisited-An-updated-look-at-FoxPro-Dotnet-Interop</link>
      <guid isPermaLink="false">57032</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2024/Sep/23/wwDotnetBridge-Revisited-An-updated-look-at-FoxPro-Dotnet-Interop#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2024/Sep/23/wwDotnetBridge-Revisited-An-updated-look-at-FoxPro-Dotnet-Interop</guid>
      <pubDate>Mon, 23 Sep 2024 04:34:21 GMT</pubDate>
    </item>
    <item>
      <title>West Wind Web Connection 8.0 Release Notes</title>
      <description>Web Connection 8.0 is here and this is the official release post for this new version.</description>
      <link>https://webconnection.west-wind.com/blog/posts/2024/Jun/25/West-Wind-Web-Connection-80-Release-Notes</link>
      <guid isPermaLink="false">9179</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2024/Jun/25/West-Wind-Web-Connection-80-Release-Notes#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2024/Jun/25/West-Wind-Web-Connection-80-Release-Notes</guid>
      <pubDate>Tue, 25 Jun 2024 21:05:49 GMT</pubDate>
    </item>
    <item>
      <title>West Wind Client Tools 8.0 Release Notes</title>
      <description>West Wind Client Tools 8.0 has released as a major version rollup release. Here's all that's new and fixed.</description>
      <link>https://webconnection.west-wind.com/blog/posts/2024/May/30/West-Wind-Client-Tools-80-Release-Notes</link>
      <guid isPermaLink="false">9177</guid>
      <author> (Rick Strahl)</author>
      <comments>https://webconnection.west-wind.com/blog/posts/2024/May/30/West-Wind-Client-Tools-80-Release-Notes#Comments</comments>
      <guid>https://webconnection.west-wind.com/blog/posts/2024/May/30/West-Wind-Client-Tools-80-Release-Notes</guid>
      <pubDate>Thu, 30 May 2024 17:19:04 GMT</pubDate>
    </item>
  </channel>
</rss>