Root > Solving bugs in your code > Managing bug reports in issue tracker > Bug trackers setup > Jira/BitBucket setup > Using HTTP upload

Using HTTP upload to post bugs to Jira

Previous pageReturn to chapter overviewNext page   

This article is part of Managing bug report in issue tracker series.

 

Jira allows you to submit reports anonymously without using API. This is an alternative method to submit bug reports. It is called Issue Collector in Jira. Issue collectors are available for both Jira bug tracker ("just Jira", "Jira Software") as well as Jira Service Desk ("Jira Service Management").

 

Note: anonymous submitting may be disabled - please, refer to your Jira configuration and documentation.

 

Warning: each sent report will be tracked individually - as new issue in Jira.

 

Below are examples for setting up anonymous HTTP upload for Jira.

 

 

Creating Issue Collector

1. Open properties/settings of your project in Jira;
2. Find "Issue collectors" item in project settings' menu;
3. Click on "Add issue collector":

 

 

4. Enter arbitrary name and description;
5. Set the "Issue Type" option to "Bug" (or "Incident", or "Problem");
6. Set the "Reporter" option to any account. This account will be used by default to create new bugs;
7. [Optionally] Check the "Attempt to match submitter email address" option;
8. Disable the "Collects the environment data of the user" option;
9. Set the "Trigger" option to "Custom";
10. Set the "Issue collector form" option to "Custom" and select at least "Description" and "Attach file". Optionally check "Components", "Environment", and "Affects versions". You may also check additional fields if you think you can supply values for these fields (see below);
11. Click the "Submit" button to create new issue collector.

 

 

New Issue Collector was created

 

12. Copy the provided HTML code for embedding;
13. Create new empty text file;
14. Paste the following text:

 

<html>
<body>

 
-- place the copied HTML code here --

 
<input 

  type="button" 

  id="myCustomTrigger" 

  name="myCustomTrigger" 

  value="Test Submit Bug">

 
</body>
</html>

 

15. Save text file as Test.html anywhere;
16. Double-click on the saved Test.html file to open it in a web browser;
17. You should see a single "Test Submit Bug" button. Click on it:

 

 

Testing Bug Submission Form

 

 

Inspecting the Web-Form

Right click on any element in the form and select "Inspect" or "View Source" command from the popup menu of your web browser;

 

The form that you see should be loaded as a nested frame inside:

 

<iframe id="atlwdg-frame" scrolling="no" frameborder="0" 
  src="https://your-address.atlassian.net/rest/collectors/1.0/template/form/your-collector-id?os_authType=none">

 

Ignore content of the Test.html file (including the mentioned tag), find the nested frame and work with it.

 

The frame should have a <form> tag like this:

 

<form id="jic-collector-form" class="aui " 
  action="https://your-address.atlassian.net/rest/collectors/1.0/template/custom/your-collector-id" 

  method="POST">

 

Copy an URL for the "action" property. This URL will be used as a value for the "URL" property of the "HTTP upload" settings in EurekaLog. Also, copy / remember the ID for the collector.

 

Scroll down the form and find these tags:

 

<input type="hidden" title="projectKey" value="your-project-key">
 
<input type="hidden" title="issueType" value="your-issue-id">
 

<input type="hidden" title="collectorId" value="your-collector-id">

 
<input type="hidden" name="pid" value="your-project-id">

 
<select class="select hidden multi-select-select" 
  id="components" multiple="multiple" 
  name="components" 
  size="3" 
  data-remove-null-options="true" 
  data-submit-input-val="true" 
  data-input-text="" 
  data-create-permission="true" 
  style="display: none;">
  <option title="Your-Component-1 " value="Your-Component-1-ID">
    Your-Component-1
  </option>
  <option title="Your-Component-2 " value="Your-Component-2-ID">
    Your-Component-2
  </option>
  ...
</select>
 
<select class="select  hidden  multi-select-select" 
  id="versions" 
  multiple="multiple"
  name="versions" 
  size="5" 
  data-remove-null-options="true" 
  data-submit-input-val="true" 
  data-input-text=" " 
  data-create-permission="true" 
  style="display: none;">
  <optgroup label="Released Versions">
    <option value="Your-Version-1-ID">
      Your-Version-1
    </option>
    <option value="Your-Version-2-ID">
      Your-Version-2
    </option>
    ...
  </optgroup>
  <optgroup label="Unreleased Versions">
    <option value="Your-Version-3-ID">
      Your-Version-3
    </option>
    <option value="Your-Version-4-ID">
      Your-Version-4
    </option>
    ...
  </optgroup>
</select>

 

Tags for components and versions will not be shown if you did not select the corresponding options when creating issue collector.

 

Save or write down all IDs. These will be used later (see below).

 

 

Setting up the HTTP Upload method

First, start with specifying HTTP upload method:

 

 

Typical HTTP upload setup for submitting report anonymously in Jira

 

Paste the URL that you have retrieved on the previous step while inspecting HTML for the web form.

 

Additionally, specify HTTP headers:

 

Origin: https://your-account.atlassian.net

Content-Type: application/x-www-form-urlencoded;charset=utf-8

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

Sec-Fetch-Site: same-origin

Sec-Fetch-Mode: navigate

Sec-Fetch-User: ?1

Sec-Fetch-Dest: iframe

Referer: https://your-account.atlassian.net/rest/collectors/1.0/template/form/your-collector-id?os_authType=none

 

Alternatively, you may set up these options at run-time:

 

uses
  ESend, ESendWebHTTP, EModules, EConsts, ETypes;
 

const
  GCollectorID = 'your-collector-id';
  GAccountID   = 'your-account';
 
initialization
  // Set up HTTP options
  CurrentEurekaLogOptions.SendHTTPURL := GAccountID + 

    '.atlassian.net/rest/collectors/1.0/template/custom/' + GCollectorID;
  CurrentEurekaLogOptions.SendHTTPPort := 443;
  CurrentEurekaLogOptions.SendHTTPSSL := True;
  CurrentEurekaLogOptions.SendHTTPHeaders :=
    'Origin: https://' + GAccountID + '.atlassian.net' + sLineBreak +
    'Content-Type: application/x-www-form-urlencoded;charset=utf-8' + sLineBreak +
    'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,' +

    '*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' + sLineBreak +
    'Sec-Fetch-Site: same-origin' + sLineBreak +
    'Sec-Fetch-Mode: navigate' + sLineBreak +
    'Sec-Fetch-User: ?1' + sLineBreak +
    'Sec-Fetch-Dest: iframe' + sLineBreak +
    'Referer: https://' + GAccountID + 

    '.atlassian.net/rest/collectors/1.0/template/form/' + GCollectorID +

    '?os_authType=none';
 
  // Register sender (if not already registered)
  if Pos(TELHTTPSender.ClassName, CurrentEurekaLogOptions.SenderClasses) <= 0 then
    CurrentEurekaLogOptions.AddSenderClass(TELHTTPSender.ClassName, 0);
end.

 

You can also add fixed HTTP fields. In other words, fields that will not change at run-time:

 

screenshot=

_tricky_=

webInfo=

projectKey=your-project-key

issueType=your-issue-id

pid=your-project-id

collectorId=your-collector-id

versions=your-version-id

components=your-component-id

 

(versions and components are optional fields)

 

This is only an example. Actual field names and values depend on your actual Jira collector's code. Please, study source code of your bug submission HTML form to learn which fields should be included.

 

Alternatively, you may set up these options at run-time - see below.

 

 

Setting up POST data

Second, you need to supply custom fields. Use OnCustomWebFieldsRequest event handler:

 

uses
  Registry, EEvents, ETypes, EConsts, EClasses, ESend, ESendWeb, 
  ESendWebHTTP, ESysInfo, EWebTools, EEncoding, EFileMemory, EJSON;
 

const
  GCollectorID = 'your-collector-id';
  GAccountID   = 'your-account';
  GPID         = 'your-project-id';

  GProjectKey  = 'your-project-key';

  GIssueID     = 'your-issue-id';

  GVersionID   = 'your-version-id';

  GComponentID = 'your-component-id';

 
// Defines HTTP fields for web-form
procedure SetCustomFields(const ACustom: Pointer; AExceptionInfo: TEurekaExceptionInfo;
  ASender: TObject; AWebFields: TStrings; var ACallNextHandler: Boolean);
 
  function ComposeTitle(const AOptions: TEurekaModuleOptions): String;
  var
    BugAppVersion: String;
    BugType: String;
    BugID: String;
  begin
    BugAppVersion := AOptions.CustomField[sifBugAppVersion];
    BugType := AOptions.CustomField[sifBugType];
    BugID := AOptions.CustomField[sifBugID];
 
    if BugAppVersion <> '' then
      Result := Format('%s (Bug %s; v%s)', [BugType, BugID, BugAppVersion])
    else
      Result := Format('%s (Bug %s)', [BugType, BugID]);
  end;
 
  function ComposeMessage(const AOptions: TEurekaModuleOptions): String;
  var
    BugText: String;
    ReproduceText: String;
  begin
    BugText := AOptions.CustomField[sifBugText];
    ReproduceText := AOptions.CustomField[sifStepsToReproduce];
 
    if ReproduceText <> '' then
      Result := BugText + sLineBreak + sLineBreak + ReproduceText
    else
      Result := BugText;
  end;
 
var
  Options: TEurekaModuleOptions;
  AttachedFiles: TStringList;
  Token: String;
begin
  Token := GetCollectorToken(ASender);
 
  Options := nil;
  AttachedFiles := TStringList.Create;
  try
    if ASender is TELUniversalSender then
    begin
      Options := TELUniversalSender(ASender).Options;
      AttachedFiles.Assign(TELUniversalSender(ASender).AttachedFiles);
    end
    else
      Options := TEurekaModuleOptions.Create('');
 
    // security field:
    AWebFields.Values['atl_token']  := Token;

 
    // hidden fields:
    AWebFields.Values['screenshot'] := '';
    AWebFields.Values['_tricky_']   := '';
    AWebFields.Values['webInfo']    := '';
 
    // dynamic fields:
    AWebFields.Values['summary'] := ComposeTitle(Options);
    AWebFields.Values['description'] := ComposeMessage(Options);
    AWebFields.Values['fullname'] := GetUserFullName;
    AWebFields.Values['email'] := Options.CustomField[sifUserEMail];
    AWebFields.Values['environment'] := 

      Format('Computer: %s' + sLineBreak + 

             'OS: %s' + sLineBreak + 

             'Build: %s'

        [Options.CustomField[sifMachineID], 

         Options.CustomField[sifOSType], 

         Options.CustomField[sifOSBuild]]);
    AWebFields.Values['filetoconvert'] := 

      UploadFilesForJiraCollector(ASender, AttachedFiles, Token);
 
    // editable fields - refer to page's source

    // this is only an example!

    // remove any field that you have already entered in 

    // HTTP upload's options
    AWebFields.Values['projectKey']  := GProjectKey;
    AWebFields.Values['issueType']   := GIssueID;
    AWebFields.Values['pid']         := GPID;
    AWebFields.Values['collectorId'] := GCollectorID;
    AWebFields.Values['versions']    := GVersionID;   // optional
    AWebFields.Values['components']  := GComponentID; // optional
  finally
    FreeAndNil(AttachedFiles);
    if not (ASender is TELUniversalSender) then
      FreeAndNil(Options);
  end;
end;
 
initialization
  // Define HTML content
  RegisterEventCustomWebFieldsRequest(nil, SetCustomFields);
end.

 

Note: study submission form's page source to get information on field names and values. The code above is just an example!

 

 

Retrieving Security Token

Use this function:

 

// Retrieves random generated 'atlassian.xsrf.token' cookie
function GetCollectorToken(const ASender: TObject): String;
 
  function Query(const AConnect: HINTERNET): String;
 
    function OpenRequest(const AConnect: HINTERNET): HINTERNET;
    const
      Headers = 'Accept: text/html,application/xhtml+xml,' +

                'application/xml;q=0.9,*/*;q=0.8,' +

                'application/signed-exchange;v=b3;q=0.9' + sLineBreak +
                'Sec-Fetch-Site: cross-site' + sLineBreak +        
                'Sec-Fetch-Mode: navigate' + sLineBreak +          
                'Sec-Fetch-Dest: iframe' + sLineBreak;             
    const
      SECURITY_FLAG_IGNORE_REVOCATION = $80;
    var
      dwOpenRequestFlags, dwBuffLen, dwFlags: DWord;
      bResult: Boolean;
    begin
      dwOpenRequestFlags := (
        INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP or
        INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS or
        INTERNET_FLAG_KEEP_CONNECTION or
        INTERNET_FLAG_NO_AUTO_REDIRECT or
        INTERNET_FLAG_NO_UI or
        INTERNET_FLAG_RELOAD or
        SECURITY_FLAG_IGNORE_REVOCATION or
        SECURITY_FLAG_IGNORE_UNKNOWN_CA or
        INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
        INTERNET_FLAG_IGNORE_CERT_DATE_INVALID or
        INTERNET_FLAG_NO_CACHE_WRITE or
        // SSL Settings:
        INTERNET_FLAG_SECURE or
        INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
        INTERNET_FLAG_IGNORE_CERT_DATE_INVALID);
 
      Result := HttpOpenRequest(AConnect, 'GET'

        '/rest/collectors/1.0/template/form/' + GCollectorID + 
        '?os_authType=none'nilnilnil, dwOpenRequestFlags, nil);
      CheckWinInetError(Result = nil);
 
      // SSL options:
      dwBuffLen := SizeOf(dwFlags);
      InternetQueryOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwBuffLen);
      dwFlags := (dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA);
      InternetSetOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, SizeOf(dwFlags));
 
      // Remove old headers
      HttpAddRequestHeaders(Result, 'Accept: ' + sLineBreak + 'Content-Type: ' + sLineBreak, 
        DWord(-1), HTTP_ADDREQ_FLAG_ADD or HTTP_ADDREQ_FLAG_REPLACE); 
      // Add new headers
      bResult := HttpAddRequestHeaders(Result, PChar(Headers), DWord(-1), HTTP_ADDREQ_FLAG_ADD);
      CheckWinInetError(not bResult);
    end;
 
    procedure SendRequestBody(const ARequest: HINTERNET);
    var
      BufferIn: INTERNET_BUFFERS;
      bResult: Boolean;
    begin
      FillChar(BufferIn, SizeOf(BufferIn), 0);
      BufferIn.dwStructSize := SizeOf(INTERNET_BUFFERS);
      BufferIn.dwBufferTotal := 0;
 
      bResult := HttpSendRequestEx(ARequest, @BufferIn, nil, 0, 0);
      CheckWinInetError(not bResult);
      HttpEndRequest(ARequest, nil, 0, 0);
    end;
 
    function ReadResponse(const ARequest: HINTERNET): String;
    var
      Sz: Cardinal;
      Ind: Cardinal;
      ErrCode: TSysErrorCode;
      Buffer: String;
      Cookies: TStringList;
    begin
      Sleep(10);
 
      Sz := SizeOf(ErrCode);
      ErrCode := TSysErrorCode(0);
      Ind := 0;
      if HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE, 
         @ErrCode, Sz, Ind) and
         (ErrCode div 100 <> 2) then
        raise EHTTPError.Create(0, Cardinal(ErrCode), ReadErrorMessage(ARequest, ErrCode));
 
      Sz := 1024;
      SetLength(Buffer, Sz);
      ErrCode := TSysErrorCode(0);
      Ind := 0;
      if HttpQueryInfo(ARequest, HTTP_QUERY_SET_COOKIE, Pointer(Buffer), Sz, Ind) then
        SetLength(Buffer, Sz div SizeOf(Char))
      else
        Finalize(Buffer);
 
      Cookies := TStringList.Create;
      try
        Cookies.Text := Buffer;
        Result := Trim(Cookies.Values['atlassian.xsrf.token']); 
        if Pos(';', Result) > 0 then
        begin
          SetLength(Result, Pos(';', Result) - 1);
          Result := Trim(Result);
        end;
      finally
        FreeAndNil(Cookies);
      end;
    end;
 
    procedure CloseRequest(var ARequest: HINTERNET);
    begin
      if Assigned(ARequest) then
      begin
        InternetCloseHandle(ARequest);
        ARequest := nil;
      end;
    end;
 
  var
    Request: HINTERNET;
  begin
    Request := OpenRequest(AConnect);
    try
      SendRequestBody(Request);
      Result := ReadResponse(Request);
    finally
      CloseRequest(Request);
    end;
  end;
 
var
  Internet, Connect: HINTERNET;
  UserAgent: String;
begin
  if not InitWebTools then
  begin
    Result := '';
    Exit;
  end;
  try
    if ASender is ESendWeb.TELWebSender then
      UserAgent := TELWebSender(ASender).UserAgent
    else
      UserAgent := EUserAgent;
    Internet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nilnil, 0);
    CheckWinInetError(Internet = nil);
    try
      Connect := InternetConnect(Internet, GAccountID + '.atlassian.net'
        443, nilnil, INTERNET_SERVICE_HTTP, 
        INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_NO_CACHE_WRITE, 0);
      CheckWinInetError(Connect = nil);
      try
        Result := Query(Connect);
      finally
        InternetCloseHandle(Connect);
      end;
    finally
      InternetCloseHandle(Internet);
    end;
  finally
    DoneWebTools;
  end;
end;

 

 

Uploading Bug Report file

Use this function:

 

// Uploads file to the collector
function UploadFilesForJiraCollector(const ASender: TObject; 

  const AAttachedFiles: TStrings; const AToken: String): String;
 
  function Query(const AConnect: HINTERNET; const AFile, AToken: String): String;
  var
    FS: TMemoryMappedStream;
 
    function OpenRequest(const AConnect: HINTERNET; 

      const AFile, AToken: String): HINTERNET;
    const
      DefHeaders = 'Accept: */*' + sLineBreak +                      
                   'Sec-Fetch-Site: same-origin' + sLineBreak +      
                   'Sec-Fetch-Mode: cors' + sLineBreak +             
                   'Sec-Fetch-Dest: empty' + sLineBreak +            
                   'Referer: https://' + GAccountID + 

                   '.atlassian.net/rest/collectors/1.0/template/form/' + 

                   GCollectorID + '?os_authType=none' + sLineBreak; 
    const
      SECURITY_FLAG_IGNORE_REVOCATION = $80;
    var
      dwOpenRequestFlags, dwBuffLen, dwFlags: DWord;
      Headers, Ext: String;
      Reg: TRegistry;
      ContentType: String;
      bResult: Boolean;
    begin
      dwOpenRequestFlags := (
        INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP or
        INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS or
        INTERNET_FLAG_KEEP_CONNECTION or
        INTERNET_FLAG_NO_AUTO_REDIRECT or
        INTERNET_FLAG_NO_UI or
        INTERNET_FLAG_RELOAD or
        SECURITY_FLAG_IGNORE_REVOCATION or
        SECURITY_FLAG_IGNORE_UNKNOWN_CA or
        INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
        INTERNET_FLAG_IGNORE_CERT_DATE_INVALID or
        INTERNET_FLAG_NO_CACHE_WRITE or
        // SSL Settings:
        INTERNET_FLAG_SECURE or
        INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
        INTERNET_FLAG_IGNORE_CERT_DATE_INVALID);
 
      Ext := AnsiLowerCase(ExtractFileExt(AFile));
      if Ext = '.el' then
        ContentType := 'text/plain'
      else
      if Ext = '.elf' then
        ContentType := 'text/plain'
      else
      if Ext = '.elp' then
        ContentType := 'application/zip'
      else
      if Ext = '.elx' then
        ContentType := 'text/xml'
      else
      if Ext = '.elj' then
        ContentType := 'application/json'
      else
      begin
        Reg := TRegistry.Create;
        try
          Reg.RootKey := HKEY_CLASSES_ROOT;
          if Reg.OpenKey('\' + Ext, False) then
            ContentType := Trim(Reg.ReadString('Content Type'))
          else
            ContentType := '';
          if ContentType = '' then
            ContentType := 'application/octet-stream';
        finally
          FreeAndNil(Reg);
        end;
      end;
      Headers := DefHeaders + 'Content-Type: ' + ContentType + sLineBreak;
 
      Result := HttpOpenRequest(AConnect, 'POST',
        PChar(Format('/rest/collectors/1.0/tempattachment/' + GCollectorID + 
          '?filename=%s&size=%d&atl_token=%s&formToken=null&projectId=' + GPID,
        [URLEncode(ExtractFileName(AFile)), FS.Size, AToken])), 

         nilnilnil, dwOpenRequestFlags, nil); 
      CheckWinInetError(Result = nil);
 
      // SSL options:
      dwBuffLen := SizeOf(dwFlags);
      InternetQueryOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwBuffLen);
      dwFlags := (dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA);
      InternetSetOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, SizeOf(dwFlags));
 
      // Remove old headers
      HttpAddRequestHeaders(Result, 'Accept: ' + sLineBreak + 'Content-Type: ' + sLineBreak, 
        DWord(-1), HTTP_ADDREQ_FLAG_ADD or HTTP_ADDREQ_FLAG_REPLACE);
      // Add new headers
      bResult := HttpAddRequestHeaders(Result, PChar(Headers), DWord(-1), HTTP_ADDREQ_FLAG_ADD);
      CheckWinInetError(not bResult);
    end;
 
    procedure SendRequestBody(const ARequest: HINTERNET);
    var
      BufferIn: INTERNET_BUFFERS;
      TotalSize: Integer;
      dwBytesWritten: Cardinal;
      bResult: Boolean;
    begin
      TotalSize := FS.Size;
 
      FillChar(BufferIn, SizeOf(BufferIn), 0);
      BufferIn.dwStructSize := SizeOf(INTERNET_BUFFERS);
      BufferIn.dwBufferTotal := TotalSize;
 
      bResult := HttpSendRequestEx(ARequest, @BufferIn, nil, 0, 0);
      CheckWinInetError(not bResult);
      try
        if TotalSize > 0 then
        begin
          bResult := InternetWriteFile(ARequest, FS.Memory, Cardinal(TotalSize), dwBytesWritten);
          CheckWinInetError(not bResult);
        end;
      finally
        HttpEndRequest(ARequest, nil, 0, 0);
      end;
    end;
 
    function ReadResponse(const ARequest: HINTERNET): String;
    const
      ERROR_INTERNET_TIMEOUT                = 12002;
      ERROR_INTERNET_INCORRECT_HANDLE_STATE = 12019;
    var
      Read: Cardinal;
      Data, Buf: RawByteString;
      Sz: Cardinal;
      Ind: Cardinal;
      ErrCode, SavedErrCode: TSysErrorCode;
      bResult: Boolean;
    begin
      Sleep(10);
 
      Read := 10240;
      Sz := SizeOf(Read);
      Ind := 0;
      SetLastError(TSysErrorCode(0));
      if (not HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_CONTENT_LENGTH, 
                @Read, Sz, Ind)) or (Read = 0) then
      begin
        SavedErrCode := GetLastError;
        if SavedErrCode = TSysErrorCode(12150) { Header Not Found } then
        begin
          // "Transfer-encoding: chunked"
          Read := 10240;
        end
        else
        begin
          Sz := SizeOf(ErrCode);
          ErrCode := TSysErrorCode(0);
          Ind := 0;
          if not HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE, 
                   @ErrCode, Sz, Ind) then
          begin
            SetLastError(SavedErrCode);
            CheckWinInetError(True);
          end;
 
          if ErrCode div 100 <> 2 then
            raise EHTTPError.Create(0, Cardinal(ErrCode), ReadErrorMessage(ARequest, ErrCode))
          else
          begin
            Result := '';
            Exit;
          end;
        end;
      end;
      SetLength(Buf, Read);
 
      Data := '';
      repeat
        Read := 0;
        bResult := InternetReadFile(ARequest, Pointer(Buf), Length(Buf), Read);
        CheckWinInetError(not bResult);
        if Read > 0 then
          Data := Data + Copy(Buf, 1, Read);
      until Read = 0;
 
      if Data = '' then
      begin
        Sz := SizeOf(ErrCode);
        ErrCode := TSysErrorCode(0);
        Ind := 0;
        if HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE, 
           @ErrCode, Sz, Ind) and
           (ErrCode div 100 <> 2) then
          raise EHTTPError.Create(0, Cardinal(ErrCode), ReadErrorMessage(ARequest, ErrCode));
      end;
 
      Result := UTF8ToString(Data);
    end;
 
    procedure CloseRequest(var ARequest: HINTERNET);
    begin
      if Assigned(ARequest) then
      begin
        InternetCloseHandle(ARequest);
        ARequest := nil;
      end;
    end;
 
  var
    Request: HINTERNET;
    JSON: IJSONValues;
  begin
    FS := TMemoryMappedStream.Create(AFile, GENERIC_READ);
    try
      Request := OpenRequest(AConnect, AFile, AToken);
      try
        SendRequestBody(Request);
        FreeAndNil(FS);
        JSON := JSONCreate(ReadResponse(Request));
        Result := JSON['id'];
      finally
        CloseRequest(Request);
      end;
    finally
      FreeAndNil(FS);
    end;
  end;
 
var
  Internet, Connect: HINTERNET;
  UserAgent: String;
begin
  if (AAttachedFiles = nilor
     (AAttachedFiles.Count = 0) or
     (not FileExists(AAttachedFiles[0])) or
     (not InitWebTools) then
  begin
    Result := '';
    Exit;
  end;
  try
    if ASender is ESendWeb.TELWebSender then
      UserAgent := TELWebSender(ASender).UserAgent
    else
      UserAgent := EUserAgent;
    Internet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nilnil, 0);
    CheckWinInetError(Internet = nil);
    try
      Connect := InternetConnect(Internet, GAccountID + '.atlassian.net'
        443, nilnil, INTERNET_SERVICE_HTTP, 
        INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_NO_CACHE_WRITE, 0);
      CheckWinInetError(Connect = nil);
      try
        Result := Query(Connect, AAttachedFiles[0], AToken);
      finally
        InternetCloseHandle(Connect);
      end;
    finally
      InternetCloseHandle(Internet);
    end;
  finally
    DoneWebTools;
  end;
end;

 

 

Other helpers routines

procedure CheckWinInetError(const AError: Boolean);
var
  ErrorCode: TSysErrorCode;
  ErrorMessage: String;
begin
  if AError then
  begin
    ErrorCode := GetLastError;
    ErrorMessage := FindWinINetErrorMessage(Cardinal(ErrorCode));
    if ErrorMessage = '' then
      ErrorMessage := SysErrorMessage(ErrorCode);
 
    raise EWinInetError.Create(0, Cardinal(ErrorCode), ErrorMessage);
  end;
end;
 
function ReadErrorMessage(const ARequest: HINTERNET; const ErrCode: TSysErrorCode): String;
var
  Sz: Cardinal;
  Ind: Cardinal;
  Headers: String;
  Str: TStringList;
  X: Integer;
begin
  Sz := 10240;
  SetLength(Result, Sz div SizeOf(Char));
  Ind := 0;
  if HttpQueryInfo(ARequest, HTTP_QUERY_STATUS_TEXT, Pointer(Result), Sz, Ind) then
  begin
    SetLength(Result, Sz div SizeOf(Char));
    Result := Trim(Result);
  end
  else
    Result := '';
 
  if (ErrCode = TSysErrorCode(301)) or (ErrCode = TSysErrorCode(302)) then
  begin
    Sz := 10240;
    SetLength(Headers, Sz div SizeOf(Char));
    Ind := 0;
    if HttpQueryInfo(ARequest, HTTP_QUERY_RAW_HEADERS_CRLF, Pointer(Headers), Sz, Ind) then
    begin
      SetLength(Headers, Sz div SizeOf(Char));
      Headers := Trim(Headers);
      Str := TStringList.Create;
      try
        Str.Text := AnsiLowerCase(StringReplace(Headers, ': ''=', [rfReplaceAll])); // Do Not Localize
        for X := 0 to Str.Count - 1 do
          if Str.Names[X] = 'location' then // Do Not Localize
          begin
            Result := Trim(Result + sLineBreak + Str.ValueFromIndex[X]);
            Break;
          end;
 
      finally
        FreeAndNil(Str);
      end;
    end;
  end;
 
  if Result = '' then
    Result := HTTPErrorMessage(Cardinal(ErrCode))
  else
    Result := HTTPErrorMessage(Cardinal(ErrCode), Result);
end;

 

 

See also:




Send feedback... Build date: 2023-09-11
Last edited: 2023-08-09
PRIVACY STATEMENT
The documentation team uses the feedback submitted to improve the EurekaLog documentation. We do not use your e-mail address for any other purpose. We will remove your e-mail address from our system after the issue you are reporting has been resolved. While we are working to resolve this issue, we may send you an e-mail message to request more information about your feedback. After the issues have been addressed, we may send you an email message to let you know that your feedback has been addressed.


Permanent link to this article: https://www.eurekalog.com/help/eurekalog/jira_and_http_upload.php