Agha
Agha

Reputation: 3

Delphi TIdTcpServer get POST parameters sent by browser

I am developing a simple Web Server to communicate with my Web App which runs in a browser.

I managed to create a IdTcpServer and get the RequestHeaders submitted by the browser. I don't know how it's possible to get POST parameters of the form.

here is my code so far:

procedure TMyServer.WebServerExecute(AContext: TIdContext);
var
    RawData: String;
    TRequestHeader: TStringList;
begin
    TRequestHeader := TStringList.Create;

    // GET Request Headers
    RawData := AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8);
    while RawData <> '' do
      begin
        TRequestHeader.Add(RawData);
        RawData := AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8);
      end;

    // How To get POST or GET Parameters of the HTML form?
    // ...

    // Respond to the Client
    Respond(...);

    TRequestHeader.Free;
end;

Upvotes: 0

Views: 1114

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596672

Once you have the request headers, you need to analyze them to know whether a message body even exists, and in what format it is encoded in so you can read it properly (see RFC 2616 section 4.4), eg:

procedure TMyServer.WebServerConnect(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
end;

procedure TMyServer.WebServerExecute(AContext: TIdContext);
var
  ReqLine, Value: String;
  I: Integer;
  Size: Int64;
  TRequestHeader: TIdHeaderList;
begin
  TRequestHeader := TIdHeaderList.Create;
  try
    // GET Request Line ...
    ReqLine := AContext.Connection.IOHandler.ReadLn;

    // TODO: parse ReqLine as needed to extract HTTP version, resource, and query params ...

    // GET Request Headers ...
    repeat
      Value := AContext.Connection.IOHandler.ReadLn;
      if Value = '' then Break;
      TRequestHeader.Add(Value);
    until False;

    // alternatively:
    // AContext.Connection.IOHandler.Capture(TRequestHeader, '', False);

    // get POST or GET data ...

    Value := TRequestHeader.Values['Transfer-Encoding'];
    if (Value <> '') and (not TextIsSame(Value, 'identity')) then
    begin
      repeat
        Value := AContext.Connection.IOHandler.ReadLn;

        I := Pos(';', Value);
        if I > 0 then begin
          Value := Copy(Value, 1, I - 1);
        end;

        Size := StrToInt64('$' + Trim(S));
        if Size = 0 then begin
          Break;
        end;

        // read specified number of bytes as needed ...

        AContext.Connection.IOHandler.ReadLn; // read CRLF at end of chunk data
      until False;

      // read trailer headers
      repeat
        Value := AContext.Connection.IOHandler.ReadLn;
        if (Value = '') then Break;
        // update TRequestHeader as needed...
      until False;  
    end
    else
    begin
      Value := TRequestHeader.Values['Content-Length'];
      if Value = '' then
      begin
        // respond with 411 error
        Exit;
      end;

      Size := StrToInt64(Value);

      // read specified number of bytes as needed ...
    end;

    // process request as needed, based on the value of
    // ReqLine, TRequestHeader.Values['Content-Type'], etc ...

    // Respond to the Client
    Respond(...);
  finally
    TRequestHeader.Free;
  end;
end;

That being said, Indy has a TIdHTTPServer component that handles the hard work of implementing the HTTP protocol for you (which is not as trivial a task as you think it is). You should not be using TIdTCPServer for this.

You can assign a handler to the TIdHTTPServer.OnCommandGet event and use the provided ARequestInfo and AResponseInfo parameters as needed. The request headers will be in the ARequestInfo.RawHeaders property, and various sub-properties (ARequestInfo.ContentType, ARequestInfo.ContentLength, etc). The GET/POST data will be in the ARequestInfo.QueryParams, ARequestInfo.FormParams, and ARequestInfo.PostStream properties accordingly, eg:

procedure TMyServer.WebServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  // use ARequestInfo.RawHeaders and ARequestInfo.QueryParams as needed ...

  if ARequestInfo.CommandType = hcPOST then
  begin
    if IsHeaderMediaType(ARequestInfo.ContentType, 'application/x-www-form-urlencoded') then
    begin
      // use ARequestInfo.FormParams as needed ...
    end
    else begin
      // use ARequestInfo.PostStream as needed ...
    end;
  end else
  begin
    // process GET/HEAD requests as needed ...
  end;

  // Respond to the Client, by populating AResponseInfo as needed ...
end;

Upvotes: 1

Related Questions