I am trying to use Indy to serve Javascript (deploying a Swagger UI to render API documentation).
procedure TfmMain.SendJavaScriptFileResponse(AResponseInfo: TIdHTTPResponseInfo; AFileName: String);
begin
AResponseInfo.ContentType := 'application/javascript';
AResponseInfo.CharSet := 'utf-8';
var LFileContents := TStringList.Create;
try
LFileContents.LoadFromFile(AFileName);
AResponseInfo.ContentText := LFileContents.Text;
finally
LFileContents.Free;
end;
end;
When the browser receives the Javascript and attempts to run it, I get a syntax error:
Uncaught SyntaxError: illegal character U+20AC
The respoinsde headers received from the Indy IdHttpServer look like so:
HTTP/1.1 200 OK
Connection: close
Content-Encoding: utf-8
Content-Type: application/javascript; charset=utf-8
Content-Length: 1063786
Date: Sun, 05 Feb 2023 20:45:56 GMT
However, when I serve the exact same Javascript files via my hosted website, the Javascript runs fine in the browser with no errors.
Is there a setting or character set I need to use when sending Javascript files using the Indy HTTP server?
You are loading the Javascript from a file into a string
, and then you are sending that string
to the client. That requires 2 data conversions at runtime - from the file's encoding to UTF-16 in memory, and from UTF-16 to the specified AResponseInfo.Charset
on the data transmission to the client. Either one of those conversions can fail if you are not careful.
In memory, a string
in Delphi 2009+ is always UTF-16 encoded, but you are not specifying the file's encoding when loading the file into the TStringList
. So, if the file uses an encoding other than ASCII (say, UTF-8), does not have a BOM, and contains any non-ASCII characters (say, the Euro sign €
), then TStringList
WILL NOT decode the file into UTF-16 correctly. In which case, you MUST specify the file's actual encoding, eg:
procedure TfmMain.SendJavaScriptFileResponse(
AResponseInfo: TIdHTTPResponseInfo;
const AFileName: String);
begin
AResponseInfo.ContentType := 'application/javascript';
AResponseInfo.CharSet := 'utf-8';
var LFileContents := TStringList.Create;
try
LFileContents.LoadFromFile(AFileName, TEncoding.UTF8); // <-- HERE
AResponseInfo.ContentText := LFileContents.Text;
finally
LFileContents.Free;
end;
end;
Another option is to send the actual file itself, without having to load and decode it into memory first, eg:
procedure TfmMain.SendJavaScriptFileResponse(
AContext: TIdContext;
AResponseInfo: TIdHTTPResponseInfo;
const AFileName: String);
begin
AResponseInfo.ContentType := 'application/javascript';
AResponseInfo.CharSet := 'utf-8';
AResponseInfo.ServeFile(AContext, AFileName);
end;
Either way, utf-8
is not a valid value for the HTTP Content-Encoding
header. Indy does not assign any value to that header by default, so you must be assigning it manually. Don't do that in this case.