HighQualityWaschbaer
HighQualityWaschbaer

Reputation: 51

Golang http client always performs get requests and does not post

I'm currently experiencing a problem that really frustrates me and where i absolutely can't see anny issues with my code.

What i'm trying to achieve is to send a http POST message to a mockup of an iDrac i wrote (both softwares written in golang) to control the mockup's powerstate, but no matter what i configure for the request, the mockup always receives get requests with an empty body.

The function i create and send the request with:

func (iDrac *IDrac) SetPowerstate(powerstate string) error {

    //Create reset type json string
    var jsonStr = `{"ResetType":"` + powerstate + `"}`

    //Create the request with auth header
    req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
    if reqErr != nil {
        return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
    }
    req.Header.Set("Content-Type", "application/json; charset=UTF-8")

    //Make the request
    resp, doErr := http.DefaultClient.Do(req)
    if doErr != nil {
        return fmt.Errorf("COULD_NOT_SEND_POST_REQUEST_TO_IDRAC_API" + doErr.Error())
    }

    //Check if the request was successful
    if resp.StatusCode != 200 {
        return fmt.Errorf("COULD_NOT_CHANGE_SERVER_POWER_STATUS_OVER_IDRAC HTTP:" + resp.Status)
    }

    return nil
}

The helper function i use to build the request with:

func (iDrac *IDrac) buildHttpRequestWithAuthorizationHeader(method string, url string, content string) (*http.Request, error) {

    req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(content)))
    if err != nil {
        return nil, err
    }

    req.Header.Add("Authorization", "Basic "+iDrac.hashedCredentials)
    return req, nil
}

And finally the function where the mockup proccesses the request:

func handlePerformReset(w http.ResponseWriter, r *http.Request) {
    garbagelog.Log("Handling reset perform request from " + r.RemoteAddr)

    if r.Method != http.MethodPost {
        garbagelog.Log("Invalid http method! Got " + r.Method + " expected POST")
        w.WriteHeader(405)
        return
    }
    if !checkAuthorization(r.BasicAuth()) {
        w.WriteHeader(401)
        return
    }

    var resetType nidrac.ResetType
    err := json.NewDecoder(r.Body).Decode(&resetType)
    if err != nil {
        garbagelog.Log("Could not decode reset type: " + err.Error())
        w.WriteHeader(422)
        return
    }

    iDracMock.PerformResetAction(resetType.ResetType)
    garbagelog.Log(">>>>SEVERSTATE: " + iDracMock.PowerState().PowerState + "<<<<")

    w.WriteHeader(200)
}

The type the iDrac mock tries to convert the body to:

type ResetType struct {
    ResetType string
}

It works flawlessly when i try to reach the endpoint with postman: iDrac mockup log confirming the successful request

Postnam request configuration: Postman request configuration

But it somehow does not work when i try making the request with go code: iDrac mockup log saying that the http method is invalid (because it's get instead of post)

I spent two hours trying to find a solution but i somehow did not find a post with someone having the same problem that i have.

Edit: Corrected old code. The problem remains even with the silly mistake fixed:

//Create the request with auth header
    req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
    if reqErr != nil {
        return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
    }

How i build the routes in the iDrac mockup:

http.Handle("/", http.HandlerFunc(handleDefault))
    http.Handle("/reset", http.HandlerFunc(handleReset))
    http.Handle("/redfish/v1/Systems/System.Embedded.1", http.HandlerFunc(handlePowerStateGet))
    http.Handle("/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset", http.HandlerFunc(handlePerformReset))

    garbagelog.Log("Starting webserver")
    err := http.ListenAndServeTLS(":443", currentConfig.CertFile, currentConfig.PrivKeyFile, nil)
    if err != nil {
        garbagelog.Log("Could not serve TLS: " + err.Error())
    }

In both requests, the one created in my iDrac comms module and the one received by the iDrac mockup, did confirm that the requested path is:

r.URL.Path = /redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset

Upvotes: 0

Views: 1770

Answers (2)

HighQualityWaschbaer
HighQualityWaschbaer

Reputation: 51

I found the problem:

The constants i built the urls with were defined like this:

const (
    apiBaseURL            = "/redfish/v1/"
    apiChassisURL         = "/Systems/System.Embedded.1"
    apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)

Leading to a url that looks like this:

https://host/redfish/v1//Systems/System.Embedded.1/Actions/ComputerSystem.Reset (Notice the double // between v1 and Systems)

So i've fixed it:

const (
    apiBaseURL            = "/redfish/v1"
    apiChassisURL         = "/Systems/System.Embedded.1"
    apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)

And everything works correctly: Test results showing that every test was successful

I thank everyone for their input for helping me not lose my mind completely.

Upvotes: 3

Jason
Jason

Reputation: 43

based on your screenshot, I can see you are using a "POST" request in your postman, but in your code is "GET".

I think nothing wrong with the code but the method only :)

Upvotes: 1

Related Questions