On 13/10/15 5:56 AM, holo wrote:
@Rikki:

If you didn't need to make it easily changeable I would say not even
bother with OOP at all.

Basically that what i had was enough for me and on top of that i could
build my app. It need to just periodically check for new instances and
if they are started or stopped and count "up and running time" for
billing purpose. But like i said i want to learn programming in D and
basically OOP too so i want to make it "proper way".

BTW: i think right now i understand what tuple is, but still don't know
for what to duplicate struct functionalities :). Those Templates still
don't understand but i hope that will came naturally with time and
practice. eg.. they are very similar to normal functions but you can
call it with types not only attributes.. strange ;)

I red yours advises and try to create according to it my own first class.

I moved time functions and variables to method "go" they need to be
current as possible when im sending request, if wont authorization could
not pass.. so i think they shouldn't be in constructor.

I moved some other variables too, and created interface.

 From all that things came out such monster which is working and doing
its job :)

module sigawsv4;

import std.stdio, std.process;
import std.digest.sha, std.digest.hmac;
import std.string;
import std.conv;
import std.datetime;
import std.net.curl;

interface credential
{
     int go();
}

class sigv4 : credential
{
     //could be changed to take some structure as parameter instead of
such ammount of attributes

     this(string methodStr = "GET", string serviceStr = "ec2", string
hostStr = "ec2.amazonaws.com", string regionStr = "us-east-1", string
endpointStr = "https://ec2.amazonaws.com";, string payloadStr = "",
string parmStr = "Action=DescribeInstances&Version=2013-10-15")
     {

         this.method = methodStr;
         this.service = serviceStr;
         this.host = hostStr;
         this.region = regionStr;
         this.endpoint = endpointStr;
         this.payload = payloadStr;
         this.request_parameters = parmStr;

         this.accessKey = environment.get("AWS_ACCESS_KEY");
         this.secretKey = environment.get("AWS_SECRET_KEY");
     }

     public:
         string method;
         string service;
         string host;
         string region;
         string endpoint;
         string payload;
         string request_parameters;


         int go()
         {
             //time need to be set when we are sending request not before
             auto currentClock = Clock.currTime(UTC());
             auto currentDate = cast(Date)currentClock;
             auto curDateStr = currentDate.toISOString;
             auto currentTime = cast(TimeOfDay)currentClock;
             auto curTimeStr = currentTime.toISOString;
             auto xamztime = curDateStr ~ "T" ~ curTimeStr ~ "Z";

             canonicalURI = "/";
             canonicalQueryString = request_parameters;
             canonicalHeadersString =  "host:" ~ this.host ~ "\n" ~
"x-amz-date:" ~ xamztime ~ "\n";
             signedHeaders = "host;x-amz-date";

             auto canonicalRequest = getCanonicalRequest(canonicalURI,
canonicalQueryString, canonicalHeadersString, signedHeaders);

             string credentialScope = curDateStr ~ "/" ~ region ~ "/" ~
service ~ "/" ~ "aws4_request";

             string stringToSign = algorithm ~ "\n" ~ xamztime ~ "\n" ~
credentialScope ~ "\n" ~ sha256Of(canonicalRequest).toHexString.toLower;

             auto signingKey = getSignatureKey(secretKey, curDateStr,
region, service);

             string signature = hmac!SHA256(stringToSign.representation,
signingKey).toHexString.toLower;

             string authorizationHeader = algorithm ~ " " ~
"Credential=" ~ accessKey ~ "/" ~ credentialScope ~ ", " ~
"SignedHeaders=" ~ signedHeaders ~ ", " ~ "Signature=" ~ signature;


             auto client = HTTP(endpoint ~ "?" ~ canonicalQueryString);
             client.method = HTTP.Method.get;
             client.addRequestHeader("x-amz-date", xamztime);
             client.addRequestHeader("Authorization", authorizationHeader);
             auto content = client.perform();

             return content;
         }

     private:
         const algorithm = "AWS4-HMAC-SHA256";

         string accessKey;
         string secretKey;

         string currentClock;
         string currentDate;
         string curDateStr;
         string currentTime;
         string curTimeStr;
         string xamztime;

         string canonicalURI;
         string canonicalQueryString;
                string canonicalHeadersString;
                string signedHeaders;



         alias sign = hmac!SHA256;

         auto getSignatureKey(string key, string dateStamp, string
regionName, string serviceName)
         {
             auto kString = ("AWS4" ~ key).representation;
             auto kDate = sign(dateStamp.representation, kString);
             auto kRegion = sign(regionName.representation, kDate);
             auto kService = sign(serviceName.representation, kRegion);
             auto kSigning = sign("aws4_request".representation, kService);

             return kSigning;
         }


         auto getCanonicalRequest(string canonicalURI, string
canonicalQueryString, string canonicalHeadersString, string signedHeaders)
         {
             string payloadHash = sha256Of(payload).toHexString.toLower;
             string canonicalRequest = method ~ "\n" ~ canonicalURI ~
"\n" ~ canonicalQueryString ~ "\n" ~ canonicalHeadersString ~ "\n" ~
signedHeaders ~ "\n" ~ payloadHash;
             return canonicalRequest;
         }
}

void main()
{
     sigv4 sig = new sigv4();
     writeln(sig.go());
}

I want to ask you for  advises what i could do with that class to make
it looks more "pro"/elegant/build in "proper way". Probably there are
lot of mistakes which all beginners are doing.

eg.: Did i use interface correctly?

You are reasonably close:
credential sig = new sigv4();

Although go is not really doing what I expect it to do.
To me go should be dedicated to performing a request given what ever you need for just that request. The class sigv4 is your global state aka what doesn't change per request. To me what go returns is whatever is the common denominator for what you need from it is.

From what I can see, you probably want go to take the payload as an argument.
That way you can reuse an instance of sigv4. Which is the ultimate goal.

Reply via email to