EldoS | Feel safer!

Software components for data protection, secure storage and transfer

TElHTTPSClient Post Sample

Also by EldoS: Rethync
The cross-platform framework that simplifies synchronizing data between mobile and desktop applications and servers and cloud storages
#28362
Posted: 02/10/2014 16:55:10
by  rogerg
I am trying to use the TElHTTPSClient class to upload to UPS's PLD server.
The output I am getting from the class doesn't look like the example file they provided and it's not working. Need some help in changing the output. Was hoping there was something I could do other than writing all the output myself to a stream of some kind. I am starting with the consoleHTTPSPost sample that came with the latest download.

Using C++, Visual Studio 2008/2010

Here is the sample output from UPS:
Code
POST /hapld/tos/kdwhapltos HTTP/1.1
Host: www.pld-certify.ups.com
Content-type: multipart/mixed; boundary=BOUNDARY
Content-length: 1040

--BOUNDARY
Content-type: application/x-www-form-urlencoded
Content-length: 140

AppVersion=1.0&AcceptUPSLicenseAgreement=Yes&ResponseType=application/x-ups-pld&VersionNumber=V4R1&UserId=useridvalue&Password=passwordvalue

--BOUNDARY
Content-type: application/x-ups-binary
Content-length: 719

020082 2.0 2002101700000000000010500 000000001*AA0A1754 1234567002000001*BA1z1234560100002352
00001+0000000000000010 +000
0000000000000LBS01PRE10 3INUSD000001*CA18ATTENTION
DELIVERY 234 SOME LOCAL ST ME CITY NJ 07652
US12015551212 *PA1z1234560100002352
02+0000010
*SA000004

--BOUNDARY--


Here is the output I am currently generating:
Code
EldoS HTTPS post client console demo.
Uploading: X:\Choctaw\Documents\Manifests\UPS\00001HostUpload201309041830.txt
POST /hapld/tos/kdwhapltos HTTP/1.0
Content-Type: multipart/form-data; boundary=---------------------------114782935826962
Host: www.pld-certify.ups.com
Authorization: Basic UlVCSU4xMzpXSElURQ==
User-Agent: SecureBlackbox
Accept-Encoding: gzip, deflate
Content-Length: 3639

-----------------------------114782935826962
Content-Disposition: form-data; name="AppVersion"

2.2
-----------------------------114782935826962
Content-Disposition: form-data; name="AcceptUPSLicenseAgreement"

Yes
-----------------------------114782935826962
Content-Disposition: form-data; name="ResponseType"

application/x-ups-pld
-----------------------------114782935826962
Content-Disposition: form-data; name="VersionNumber"

V4R1
-----------------------------114782935826962
Content-Disposition: form-data; name="UserId"

userdata
-----------------------------114782935826962
Content-Disposition: form-data; name="Password"

password
-----------------------------114782935826962
Content-Disposition: form-data; name="userfile"; filename="X:\Choctaw\Documents\Manifests\UPS\00001HostUpload201309041830.txt"
Content-Type: application/octet-stream

020082 2.0 2002101700000000000010500 000000001*AA0A1754 1234567002000001*BA1z1234560100002352
00001+0000000000000010 +000
0000000000000LBS01PRE10 3INUSD000001*CA18ATTENTION
DELIVERY 234 SOME LOCAL ST ME CITY NJ 07652
US12015551212 *PA1z1234560100002352
02+0000010
*SA000004

-----------------------------114782935826962--


NOTE: I have replaced the data in the file being uploaded so the size will be incorrect.
Here is the snippet of code used:
Code
         HTTPSClient.set_OnProgress(OnProgress, NULL);
         HTTPSClient.set_OnCertificateValidate(OnCertificateValidate, NULL);
         HTTPSClient.set_OnSendData(OnSendData, NULL);

         HTTPSClient.set_RequestParameters(requestParams);

         TFileStream stream (inputFilename, fmOpenRead);

         TElStringList* SL = new TElStringList();
         //SL->Add("Upload");
         SL->Add("AppVersion=2.2");
         SL->Add("AcceptUPSLicenseAgreement=Yes");
         SL->Add("ResponseType=application/x-ups-pld");
         SL->Add("VersionNumber=V4R1");
         SL->Add("UserId=userdata");
         SL->Add("Password=password");

         TElStringList* response;
         try
         {
            std::cout << "Uploading: " << inputFilename << std::endl;
            HTTPSClient.Post(url, *SL, "userfile", inputFilename, stream, "", true);
            response = HTTPSClient.get_ResponseHeaders();
            for (int i = 0; i < response->get_Count(); i++)
            {
               std::string OutResult;
               response->get_ValueFromIndex(i, OutResult);
               std::cout <<  OutResult << std::endl;
            }
         }
         catch (SBException E)
         {
            std::cout << "Exception happened during HTTP post: " << E.what() << std::endl;
         }


Thank you
#28366
Posted: 02/11/2014 01:19:42
by Eugene Mayevski (EldoS Corp.)

Thank you for detailed description of the problem.

In your sample SecureBlackbox produces the same output as the browser would produce when posting an HTML form.

Unfortunately it's not clear what exactly format UPS expects. It looks like MIME but I can hardly understand if it has any use outside of UPS itself. So at the moment the only option is to compose the contents yourself.

Maybe we can extend the components to generate such format, IF it is used anywhere.

Can you please share a reference to the document where the requirements for the data are specified?


Sincerely yours
Eugene Mayevski
#28369
Posted: 02/11/2014 09:28:15
by  rogerg
The UPS request document is not very good. It's missing a description of the parameters keys entirely so the sample is all I have to work with. There is a description of how to calculate the various "Content-length" values for each section, but that is about it. Also, I am not sure if I am allowed to share it.

I have started trying to use a stream to generate the format I need to send. However, it doesn't look like anything is being sent. If calling the Post method that takes a stream, is the OnSendData callback getting called? The text I am trying to put through the stream is not getting echoed there. It's possible I am not using the Memory stream correctly as I am not used to using them at all.

Code
         TMemoryStream source;
         source.Write("Content-type: multipart/mixed; boundary=BOUNDARY\n", 50);
         source.Write("Content-length: 1040\n", 22);
         source.Write("AppVersion=1.0&AcceptUPSLicenseAgreement=Yes&ResponseType=application/x-ups-pld&VersionNumber=V4R1&UserId=useridvalue&Password=passwordvalue", 140);

         HTTPSClient.Post(url, source, true);


This is basically what I have now (try..catch removed).
Am I way off?

Even if the lengths of the strings are off, and the values don't make sense, should I not see something getting echoed through the OnSendData?

Roger
#28370
Posted: 02/11/2014 09:29:34
by Eugene Mayevski (EldoS Corp.)

You need to reset stream position after you finish writing.


Sincerely yours
Eugene Mayevski
#28371
Posted: 02/11/2014 14:39:41
by  rogerg
Can I prevent the top section Content-Length? Or override the Content type and boundary? I am adding everything after the "Content-Length: 1145", but that is apparently messing up on the server side trying to parse the message. Also what is the "Connection: Close"?

Code
POST /hapld/tos/kdwhapltos HTTP/1.1
Host: www.pld-certify.ups.com
User-Agent: SecureBlackbox
Accept-Encoding: gzip, deflate
Connection: Close
Content-Length: 1145

Content-type: multipart/mixed; boundary=BOUNDARY
Content-length: 1048

--BOUNDARY
Content-type: application/x-www-form-urlencoded
Content-length: 128
#28372
Posted: 02/11/2014 14:46:26
by Eugene Mayevski (EldoS Corp.)

You compose what goes in lines 8 and below.

Line 7 is a separator empty line. It's added by the component.

Lines 1-6 are composed by the component based on information you've provided (including the stream with the data that you've passed). Some of those headers can be modified via TElHTTPSClient.RequestParameters property, others are an integral part of the protocol (such as "Connection: close" header that indicates that the server should close connection after returning response).


Sincerely yours
Eugene Mayevski
#28373
Posted: 02/11/2014 15:35:35
by  rogerg
So I removed my code to generate lines 8 & 9 from last post, and added this code to change the header that got inserted between lines 3 & 4.

Code
         TElHTTPRequestParams * params;
         params = HTTPSClient.get_RequestParameters();

         params->set_ContentType("multipart/mixed; boundary=BOUNDARY");

         HTTPSClient.set_RequestParameters(params);


I am not sure if it was required to get the parameters before setting new ones, but it seemed like a good idea.

This is now working for me as the UPS test site accepted the test data. Now I just have to make most of the data dynamic.

Thank you for your help.
#28374
Posted: 02/11/2014 15:45:35
by Eugene Mayevski (EldoS Corp.)

No, you don't need to set RequestParameters. That object exists internally and is managed internally. I think that it's not even changed if you call set_ method (the C++ wrapper generator might have generated it to be a dummy).

So you just read the existing value as you did in line 2 and change the property. That's all.


Sincerely yours
Eugene Mayevski
Also by EldoS: CallbackRegistry
A component to monitor and control Windows registry access and create virtual registry keys.

Reply

Statistics

Topic viewed 4310 times

Number of guests: 1, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!