title: Indy MIME decoding of Multipart/Form-Data Requests returns trailing CR/LF
body: <p>Indy 10.6 revision 5128 seems to contain a change which breaks the code below for HTTP form uploads. </p>
  
  <p>The received data contains two additional bytes at the end, a CR/LF pair.</p>
  
  <p>Reading through the changed code lines between 5127 and 5128 did not bring me to the root cause. </p>
  
  <p>I will try to debug it when I find the time and post the result here (but maybe somebody is faster).</p>
  
  <p>Here is a stand-alone demo application which shows a HTML upload form at <code>http://127.0.0.1:8080</code></p>
  
  <pre><code>program IndyMultipartUploadDemo;
  
  {$APPTYPE CONSOLE}
  
  uses
    IdHTTPServer, IdCustomHTTPServer, IdContext, IdSocketHandle, IdGlobal,
    IdMessageCoder, IdGlobalProtocols, IdMessageCoderMIME, IdMultiPartFormData,
    SysUtils, Classes;
  
  type
    TMimeHandler = procedure(var VDecoder: TIdMessageDecoder;
      var VMsgEnd: Boolean; const Response: TIdHTTPResponseInfo) of object;
  
    TMyServer = class(TIdHTTPServer)
    private
      procedure ProcessMimePart(var VDecoder: TIdMessageDecoder;
        var VMsgEnd: Boolean; const Response: TIdHTTPResponseInfo);
      function IsHeaderMediaType(const AHeaderLine, AMediaType: String): Boolean;
      function MediaTypeMatches(const AValue, AMediaType: String): Boolean;
      function GetUploadFolder: string;
      procedure HandleMultipartUpload(Request: TIdHTTPRequestInfo; Response:
       TIdHTTPResponseInfo; MimeHandler: TMimeHandler);
    public
      procedure InitComponent; override;
      procedure DoCommandGet(AContext: TIdContext;
        ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); override;
    end;
  
  procedure Demo;
  var
    Server: TMyServer;
  begin
    ReportMemoryLeaksOnShutdown := True;
  
    Server := TMyServer.Create;
    try
      try
        Server.Active := True;
      except
        on E: Exception do
        begin
          WriteLn(E.ClassName + ' ' + E.Message);
        end;
      end;
      WriteLn('Hit any key to terminate.');
      ReadLn;
    finally
      Server.Free;
    end;
  end;
  
  procedure TMyServer.InitComponent;
  var
    Binding: TIdSocketHandle;
  begin
    inherited;
  
    Bindings.Clear;
    Binding := Bindings.Add;
    Binding.IP := '127.0.0.1';
    Binding.Port := 8080;
  
    KeepAlive := True;
  end;
  
  procedure TMyServer.DoCommandGet(AContext: TIdContext;
    ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  begin
    AResponseInfo.ContentType := 'text/html';
    AResponseInfo.CharSet := 'UTF-8';
  
    if ARequestInfo.CommandType = hcGET then
    begin
      AResponseInfo.ContentText :=
          '&lt;!DOCTYPE HTML&gt;' + #13#10
        + '&lt;html&gt;' + #13#10
        + ' &lt;head&gt;' + #13#10
        + ' &lt;title&gt;Multipart Upload Example&lt;/title&gt;' + #13#10
        + ' &lt;/head&gt;' + #13#10
        + ' &lt;body&gt; ' + #13#10
        + ' &lt;form enctype="multipart/form-data" method="post"&gt;' + #13#10
        + ' &lt;fieldset&gt;' + #13#10
        + ' &lt;legend&gt;Standard file upload&lt;/legend&gt;' + #13#10
        + ' &lt;label&gt;File input&lt;/label&gt;' + #13#10
        + ' &lt;input type="file" class="input-file" name="upload" /&gt;' + #13#10
        + ' &lt;button type="submit" class="btn btn-default"&gt;Upload&lt;/button&gt;' + #13#10
        + ' &lt;/fieldset&gt;' + #13#10
        + ' &lt;/form&gt;' + #13#10
        + ' &lt;/body&gt;' + #13#10
        + '&lt;/html&gt;' + #13#10;
    end
    else
    begin
      if ARequestInfo.CommandType = hcPOST then
      begin
        if IsHeaderMediaType(ARequestInfo.ContentType, 'multipart/form-data') then
        begin
          HandleMultipartUpload(ARequestInfo, AResponseInfo, ProcessMimePart);
        end;
      end;
    end;
  end;
  
  // based on code on the Indy and Winsock Forum articles
  // http://forums2.atozed.com/viewtopic.php?f=7&amp;t=10924
  // http://embarcadero.newsgroups.archived.at/public.delphi.internet.winsock/201107/1107276163.html
  
  procedure TMyServer.ProcessMimePart(var VDecoder: TIdMessageDecoder;
    var VMsgEnd: Boolean; const Response: TIdHTTPResponseInfo);
  var
    LMStream: TMemoryStream;
    LNewDecoder: TIdMessageDecoder;
    UploadFile: string;
  begin
    LMStream := TMemoryStream.Create;
    try
      LNewDecoder := VDecoder.ReadBody(LMStream, VMsgEnd);
      if VDecoder.Filename &lt;&gt; '' then
      begin
        try
          LMStream.Position := 0;
          Response.ContentText := Response.ContentText
            + Format('&lt;p&gt;%s %d bytes&lt;/p&gt;' + #13#10,
              [VDecoder.Filename, LMStream.Size]);
  
          // write stream to upload folder
          UploadFile := GetUploadFolder + VDecoder.Filename;
          LMStream.SaveToFile(UploadFile);
          Response.ContentText := Response.ContentText
            + '&lt;p&gt;' + UploadFile + ' written&lt;/p&gt;';
  
        except
          LNewDecoder.Free;
          raise;
        end;
      end;
      VDecoder.Free;
      VDecoder := LNewDecoder;
    finally
      LMStream.Free;
    end;
  end;
  
  function TMyServer.IsHeaderMediaType(const AHeaderLine, AMediaType: String): Boolean;
  begin
    Result := MediaTypeMatches(ExtractHeaderItem(AHeaderLine), AMediaType);
  end;
  
  function TMyServer.MediaTypeMatches(const AValue, AMediaType: String): Boolean;
  begin
    if Pos('/', AMediaType) &gt; 0 then begin
      Result := TextIsSame(AValue, AMediaType);
    end else begin
      Result := TextStartsWith(AValue, AMediaType + '/');
    end;
  end;
  
  function TMyServer.GetUploadFolder: string;
  begin
    Result := ExtractFilePath(ParamStr(0)) + 'upload\';
    ForceDirectories(Result);
  end;
  
  procedure TMyServer.HandleMultipartUpload(Request: TIdHTTPRequestInfo;
    Response: TIdHTTPResponseInfo; MimeHandler: TMimeHandler);
  var
    LBoundary, LBoundaryStart, LBoundaryEnd: string;
    LDecoder: TIdMessageDecoder;
    LLine: string;
    LBoundaryFound, LIsStartBoundary, LMsgEnd: Boolean;
  begin
    LBoundary := ExtractHeaderSubItem(Request.ContentType, 'boundary',
      QuoteHTTP);
    if LBoundary = '' then
    begin
      Response.ResponseNo := 400;
      Response.CloseConnection := True;
      Response.WriteHeader;
      Exit;
    end;
  
    LBoundaryStart := '--' + LBoundary;
    LBoundaryEnd := LBoundaryStart + '--';
  
    LDecoder := TIdMessageDecoderMIME.Create(nil);
    try
      TIdMessageDecoderMIME(LDecoder).MIMEBoundary := LBoundary;
      LDecoder.SourceStream := Request.PostStream;
      LDecoder.FreeSourceStream := False;
  
      LBoundaryFound := False;
      LIsStartBoundary := False;
      repeat
        LLine := ReadLnFromStream(Request.PostStream, -1, True);
        if LLine = LBoundaryStart then
        begin
          LBoundaryFound := True;
          LIsStartBoundary := True;
        end
        else if LLine = LBoundaryEnd then
        begin
          LBoundaryFound := True;
        end;
      until LBoundaryFound;
  
      if (not LBoundaryFound) or (not LIsStartBoundary) then
      begin
        Response.ResponseNo := 400;
        Response.CloseConnection := True;
        Response.WriteHeader;
        Exit;
      end;
  
      LMsgEnd := False;
      repeat
        TIdMessageDecoderMIME(LDecoder).MIMEBoundary := LBoundary;
        LDecoder.SourceStream := Request.PostStream;
        LDecoder.FreeSourceStream := False;
  
        LDecoder.ReadHeader;
        case LDecoder.PartType of
          mcptText, mcptAttachment:
            begin
              MimeHandler(LDecoder, LMsgEnd, Response);
            end;
          mcptIgnore:
            begin
              LDecoder.Free;
              LDecoder := TIdMessageDecoderMIME.Create(nil);
            end;
          mcptEOF:
            begin
              LDecoder.Free;
              LMsgEnd := True;
            end;
        end;
      until (LDecoder = nil) or LMsgEnd;
    finally
      LDecoder.Free;
    end;
  end;
  
  begin
    Demo;
  end.
  </code></pre>