Home > Blockchain >  Golang http client always performs get requests and does not post
Golang http client always performs get requests and does not post

Time:07-05

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

CodePudding user response:

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 :)

CodePudding user response:

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.

  • Related