#2 mORMot 1 » EAccessViolation at SynCrtSock.THttpServer.Destroy (6254) » 2020-02-14 00:18:59

macc2010
Replies: 2

Hello,

I have run in Delphi 10.3 update 3 the project ..\SQLite3\Samples\ThirdPartyDemos\George\REST-tester\mORMotRESTsrv.dpr and an exception is raised when I set the port 80 and click on the "Start server" Button.

I have seen the problem and is the following :

In RestServerUnit.pas this code is executed :

  fHTTPServer := TSQLHttpServer.Create(AnsiString(fServerSettings.Port), [fRestServer], '+', useBidirSocket);

After this, in SynCrtSock.pas, the fSock is created in the THttpServer constructor :

  constructor THttpServer.Create
  begin
      .....
      fSock := TCrtSocket.Bind(aPort); // BIND + LISTEN
      ....
  end;

But the line fSock := TCrtSocket.Bind(aPort) raises in the TCrtSocket.OpenBind this exception :

  raise ECrtSocket.CreateFmt('OpenBind(%s:%s,%s) failed: %s', [aServer,fPort,BINDTXT[doBind],BINDMSG[doBind]],-1);

So, when the destructor of the THttpServer occurs, an access violation is raised because fSock has a nil value, concretly here :

   destructor THttpServer.Destroy;
   begin
      ....
      if not fExecuteFinished then begin
        Sock.Close; // shutdown the socket to unlock Accept() in Execute   
        DirectShutdown(CallServer('127.0.0.1',Sock.Port,false,cslTCP,1)); // <--------------- WANING, Sock could be nil
      end;
      .............
   end;

I have applied a correction to this code :

   destructor THttpServer.Destroy;
   begin
      ....
      if not fExecuteFinished then begin
        Sock.Close; // shutdown the socket to unlock Accept() in Execute   
        if Sock <> nil then                                                               // <--------------- avoid to use Sock when it is nil
          DirectShutdown(CallServer('127.0.0.1',Sock.Port,false,cslTCP,1));
      end;
      .............
   end;

Thank you.

#4 mORMot 1 » Bug in TRawByteStringStream.Write? » 2020-02-07 21:19:15

macc2010
Replies: 2

Hello,

I have seen an issue using TRawByteStringStream.Write.

I did write a integer value, after, several string values, and finally I set the TRawByteStringStream Position to 0 and did write a new integer value  to the beginning of the stream. My surprise was that the TRawByteStringStream Size was cutted.

I think that there is a problem with the SetLength in this method :

function TRawByteStringStream.Write(const Buffer; Count: Integer): Longint;
begin
  if Count<=0 then
    Result := 0 else begin
    Result := Count;
    SetLength(fDataString,fPosition+Result);
    {$ifdef FPC}Move{$else}MoveFast{$endif}(Buffer,PByteArray(fDataString)[fPosition],Result);
    inc(FPosition,Result);
  end;
end;

The SetLength is set although the fPosition+Result not be greater than the length of fDataString.

Is it done thinking in performance?.

Would not be more correct this? :

function TRawByteStringStream.Write(const Buffer; Count: Longint): Longint;
begin
  if Count<=0 then
    Result := 0
  else begin
    Result := Count;
    if fPosition+Result > Length(fDataString) then
      SetLength(fDataString,fPosition+Result);
    {$ifdef FPC}Move{$else}MoveFast{$endif}(Buffer,PByteArray(fDataString)[fPosition],Result);
    inc(FPosition,Result);
  end;
end;

Thank you.

#5 mORMot 1 » Bug in Base64JSONStringToBytes in Delphi mobile compilers » 2017-01-10 12:00:16

macc2010
Replies: 0

Hello,

I have discovered a bug in the function Base64JSONStringToBytes that is located in the unit SynCrossPlatformJSON compiling with Delphi 10.1 Berlin update 2 and a recently installation of mormot of November 2016 and the project targeting and Android device.

The problem is that in the function Base64JSONStringToBytes the string named JSONString is accesed using 1-based indexing and in mobile compilers in Delphi this strings should be converted to access them using the rule of 0-based indexing ( you can see it in this page : http://docwiki.embarcadero.com/RADStudi … om_Desktop ). The best solution is to use the TStringHelper.Chars property that is 0-based indexing independent of the compiler, so changing the access of JSONString[x] by JSONString.Chars[x-1] the program run well targeting Win32/64 or Android/ios ( mobile devices ).

You can see the corrected function here : 

function Base64JSONStringToBytes(const JSONString: string;
  var Bytes: TByteDynArray; withBase64Magic: boolean): boolean;
var i,bits,value,x,magiclen,len: cardinal;
begin
  result := JSONString='';
  if result then
    exit;
  if withBase64Magic then
    if comparemem(pointer(JSONString),@JSON_BASE64_MAGIC,sizeof(JSON_BASE64_MAGIC)) then
      magiclen := JSON_BASE64_MAGIC_LEN else
      {$ifndef UNICODE}
      //if JSONString[1]='?' then // handle UTF-8 decoding error on ANSI Delphi
      if JSONString.Chars[0]='?' then // <----- NEW CODE
        magiclen := 1 else
      {$endif}
      exit else
    magiclen := 0; // withBase64Magic=false
  x := length(JSONString);
  len := x-magiclen;
  if len and 3<>0 then
    exit;
  if len=0 then
    Bytes := nil else begin
    if BASE64DECODE=nil then begin
      SetLength(BASE64DECODE,128);
      for i := 0 to 127 do
        BASE64DECODE[i] := -1;
      for i := 0 to high(BASE64) do
        BASE64DECODE[ord(BASE64[i])] := i;
    end;
    len := (len shr 2)*3;
    //if Base64One(JSONString[x])<0 then begin
    if Base64One(JSONString.Chars[x-1])<0 then begin // <----- NEW CODE
      dec(len);
      //if Base64One(JSONString[x-1])<0 then
      if Base64One(JSONString.Chars[x-2])<0 then // <----- NEW CODE
        dec(len);
    end;
    SetLength(Bytes,len);
    bits := 0;
    value := 0;
    len := 0;
    for i := magiclen+1 to Length(JSONString) do begin
      //x := ord(JSONString[i]); // inlined Base64One(JSONString[i])
      x := ord(JSONString.Chars[i-1]); // <----- NEW CODE
      if x>127 then
        break;
      x := cardinal(BASE64DECODE[x]);
      if integer(x)<0 then
        break;
      value := value*64+x;
      bits := bits+6;
      if bits>=8 then begin
        bits := bits-8;
        x := value shr bits;
        value := value and ((1 shl bits)-1);
        Bytes[len] := x;
        inc(len);
      end;
    end;
  end;
  result := len=cardinal(length(Bytes));
end;

I suppose that the complementary function BytesToBase64JSONString can have the same problem.

I saw the problem downloading a bitmap content as Base64 from a interface based service server. When I compiled the program targeting Win32 or Win64 the bitmap was decoded using Base64JSONStringToBytes and had a size X. But if I compiled the project targeting Android, the same bitmap had a size of X - 1. Once that I corrected the Base64JSONStringToBytes function to use the Chars property of TStringHelper class the problem solved.

Can anyone confirm me this bug please?. I am in the doubt if I am wrong.

Thank you and best regards.

#6 mORMot 1 » Progress in function TWinHttpAPI.InternalRetrieveAnswer » 2016-07-22 02:05:58

macc2010
Replies: 0

Hello Administrator,

I have done a modification to SynCrtSock.pas in order to allow customize the block size of the data that is read in TWinHttpAPI.InternalRetrieveAnswer.

The changes has been the following ( I have remarked the changes with the word "changed" )  :

  TWinHttpAPI = class(THttpRequest)
  protected
    ...
    fBlockSize : cardinal; // <------------------------- changed
    ...
  public
    ...
    /// hoy many bytes should be retrieved for each InternalRead in InternalRetrieveAnswer
    property BlockSize : cardinal
      read fBlockSize write fBlockSize; // <---------------------------- changed
  end;

...

function TWinHttpAPI.InternalRetrieveAnswer(
  var Header, Encoding, AcceptEncoding, Data: SockString): integer;
var Bytes, ContentLength, Read: DWORD;
    tmp: SockString;
begin // HTTP_QUERY* and WINHTTP_QUERY* do match -> common to TWinINet + TWinHTTP
    result := InternalGetInfo32(HTTP_QUERY_STATUS_CODE);
    Header := InternalGetInfo(HTTP_QUERY_RAW_HEADERS_CRLF);
    Encoding := InternalGetInfo(HTTP_QUERY_CONTENT_ENCODING);
    AcceptEncoding := InternalGetInfo(HTTP_QUERY_ACCEPT_ENCODING);
    // retrieve received content (if any)
    Read := 0;
    ContentLength := InternalGetInfo32(HTTP_QUERY_CONTENT_LENGTH);
    if Assigned(fOnDownload) then begin
      // download per-chunk using calback event
      Bytes := fOnDownloadChunkSize;
      if Bytes<=0 then
        Bytes := 65536; // 64KB seems fair enough by default
      SetLength(tmp,Bytes);
      repeat
        Bytes := InternalReadData(tmp,0);
        if Bytes=0 then
          break;
        inc(Read,Bytes);
        if not fOnDownload(self,Read,ContentLength,Bytes,pointer(tmp)^) then
          break; // returned false = aborted
        if Assigned(fOnProgress) then
          fOnProgress(self,Read,ContentLength);
      until false;
    end else
    if ContentLength<>0 then begin
      // optimized version reading "Content-Length: xxx" bytes
      if fBlockSize <= 0 then // <------------------------------------------ changed
        SetLength(Data,ContentLength);
      repeat
        // ------------------ changed start
        if fBlockSize > 0 then
        begin
          if Read + fBlockSize > ContentLength then
            Bytes := ContentLength - Read
          else
            Bytes := fBlockSize;
          SetLength( Data, Read + Bytes );
        end;
        // ------------------ changed end
        Bytes := InternalReadData(Data,Read);
        if Bytes=0 then begin
          SetLength(Data,Read); // truncated content
          break;
        end;
        inc(Read,Bytes);
        if Assigned(fOnProgress) then
          fOnProgress(self,Read,ContentLength);
      until Read=ContentLength;
    end else begin
      // Content-Length not set: read response in blocks of HTTP_RESP_BLOCK_SIZE or fBlockSize <------------------- changed
      repeat
        if fBlockSize <= 0 then // <------------------------------- changed
          SetLength(Data,Read+HTTP_RESP_BLOCK_SIZE)
        else
          SetLength(Data,Read+fBlockSize); // <------------------------------- changed
        Bytes := InternalReadData(Data,Read);
        if Bytes=0 then
          break;
        inc(Read,Bytes);
        if Assigned(fOnProgress) then
          fOnProgress(self,Read,ContentLength);
      until false;
      SetLength(Data,Read);
    end;
end;

These changes are very useful when I use a TSQLHttpClient to allow to consume a function of a interface based services server that returns a RawByteString that is very large, and I need that the client application show a progress during the "download" of this data. Without my modification, the OnProgress is only shown once, but If I set the new BlockSize property to 65535 for example, the progress is shown every 65535 bytes received and the user interface is updated showing a responsive progressbar.

Can you tell me if this is usefull for the synopse framework?.

Thank you and best regards.

#7 Re: mORMot 1 » THttpApiServer how to set HTTP/1.0 as response in the OnRequest event » 2016-05-18 18:31:23

ab wrote:

Set the appropriate output header.

Thank you, but in the OnRequest procedure I have the Ctxt: THttpServerRequest parameter. I have tried to set the Ctxt.OutCustomHeaders to HTTP/1.0 200 OK and Content-Type: image/jpeg but it does not work, I have sniffed the result and it always return HTTP/1.1 200 OK ..., I do not know how to set the output header. I have stopped the code when I set Result := 200 and followed the code line by line until the execution code return to the procedure THttpApiServer.Execute and I observed that the code arrives until :

EHttpApiServer.RaiseOnError(hSendHttpResponse,Http.SendHttpResponse(fReqQueue,Req^.RequestId,0,Resp^,nil,bytesSent,nil,0,nil,fLogData));

And in the Req^.Version.MajorVersion is 1 and Req^.Version.MinorVersion is 1, so I suspect that the HTTP/1.1 200 OK is responsed by the http.sys because I do not see it in any structure of the Req^ pointer.

Can you help me to set the appropiate header?.

Thank you and best regards.

#8 mORMot 1 » THttpApiServer how to set HTTP/1.0 as response in the OnRequest event » 2016-05-17 22:28:24

macc2010
Replies: 3

I have created a vcl form application and created an instance of THttpApiServer class. In the OnRequest event I would like to set the version of the response to set it as HTTP/1.0 instead of HTTP/1.1.

For example, when I receive a request in this format : GET /../jpg/image.cgi HTTP/1.1 .....
I would like to response with : HTTP/1.0 200 OK .... Content-Type: image/jpeg ....

Thank you and best regards.

Board footer

Powered by FluxBB