Golang setCookie() failed after template Execute() - templates

As a beginner of GO, I've got a situation as following:
t, err := template.ParseFiles("/template/login.tpl")
err = t.Execute(w, nil) //if executed before SetCookie
http.SetCookie(...) //failed, browser received nothing
If the sequence is changed, to SetCookie first, it'll work.
My plan was to ParseForm() username and password in login.tpl, if success, a sessionID would be sent by SetCookie. But now SetCookie() must be placed before login.tpl is Executed, which also makes ParseForm() run before login.tpl is executed :
r.ParseForm( ) //ParseForm() works even before template is executed
email := r.FormValue("email")
pass := r.FormValue("pass")
var loginPass = checkLogin(email, pass) //verify username and password
if loginPass == true { //if correct
cookie1 := http.Cookie{Name: "test", Value: "testvalue", Path: "/", MaxAge: 86400}
http.SetCookie(w, &cookie1) //setCookie works
fmt.Println("Login success: ", email)
} else { //if incorrect username or pass
fmt.Println("Please login: ", email)
}
t, err := template.ParseFiles("/template/login.tpl")
err = t.Execute(w, nil) //now template execution is here, also works
But why it should be written like this? Please anyone give me some advice! Thanks a lot.

This is a common error tied to the HTTP protocol working: the cookie is sent in a header of the HTTP respo7nse, which cannot be changed after the body start being sent.
So, when you do the t.Execute(w, nil) call, you start writing the body of the response and thus cannot add cookies afterwards.
This is the exact same reason why in PHP the session_start() call must be the very first instruction of the page, even before any whitespace.

Related

How do I test my handler which require tokens to call data services

This is the code written in the handler, which gets the token required to call the data service.
m2m, err := h.getM2MToken(ctx)
if err != nil {
return lc.SetResponse(&events.APIGatewayV2HTTPResponse{
StatusCode: http.StatusInternalServerError,
Body: "Internal Server Error (m2m)",
})
}
//Get the bearer token
userToken, err := h.getBearer(req.Headers)
if err != nil {
xray.AddError(ctx, err)
return lc.SetResponse(&events.APIGatewayV2HTTPResponse{
StatusCode: http.StatusInternalServerError,
Body: "Internal Server Error (bearer)",
})
}
My suggestion is to first try abstracting the inputs that you sent to a method
Like instead of this
userToken, err := h.getBearer(req.Headers)
You can pass specify interfaces like
type userTokenInput struct {}
uti := userTokenInput{}
userToken, err := h.getBearer(uti)
The above helps you to have control over input which makes testing easier
For network calls try using some mock HTTP client which can return expected
data you can follow this for mock HTTP client https://www.thegreatcodeadventure.com/mocking-http-requests-in-golang/
If the service does not work without a token, you will have to provide one.
If the calls you will be doing should not be seen on the real target system for whatever reason, you will need a different target system for testing.
Ask the provider if they have a test installation you can use.
Consider testing against a mock.

API Gateway HTTP client request with IAM auth with Go

Hello StackOverflow AWS Gophers,
I'm implementing a CLI with the excellent cobra/viper packages from spf13. We have an Athena database fronted by an API Gateway endpoint, which authenticates with IAM.
That is, in order to interact with its endpoints by using Postman, I have to define AWS Signature as Authorization method, define the corresponding AWS id/secret and then in the Headers there will be X-Amz-Security-Token and others. Nothing unusual, works as expected.
Since I'm new to Go, I was a bit shocked to see that there are no examples to do this simple HTTP GET request with the aws-sdk-go itself... I'm trying to use the shared credentials provider (~/.aws/credentials), as demonstrated for the S3 client Go code snippets from re:Invent 2015:
req := request.New(nil)
How can I accomplish this seemingly easy feat in 2019 without having to resort to self-cooked net/http and therefore having to manually read ~/.aws/credentials or worse, go with os.Getenv and other ugly hacks?
Any Go code samples interacting as client would be super helpful. No Golang Lambda/server examples, please, there's plenty of those out there.
Unfortunately, it seems that the library has been updated since the accepted answer was written and the solution no longer is the same. After some trial and error, this appears to be the more current method of handling the signing (using https://pkg.go.dev/github.com/aws/aws-sdk-go-v2):
import (
"context"
"net/http"
"time"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/aws/signer/v4"
)
func main() {
// Context is not being used in this example.
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
// Handle error.
}
credentials, err := cfg.Credentials.Retrieve(context.TODO())
if err != nil {
// Handle error.
}
// The signer requires a payload hash. This hash is for an empty payload.
hash := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
req, _ := http.NewRequest(http.MethodGet, "api-gw-url", nil)
signer := v4.NewSigner()
err = signer.SignHTTP(context.TODO(), credentials, req, hash, "execute-api", cfg.Region, time.Now())
if err != nil {
// Handle error.
}
// Use `req`
}
The solution below uses aws-sdk-go-v2
https://github.com/aws/aws-sdk-go-v2
// A AWS SDK session is created because the HTTP API is secured using a
// IAM authorizer. As such, we need AWS client credentials and a
// session to properly sign the request.
cfg, err := external.LoadDefaultAWSConfig(
external.WithSharedConfigProfile(profile),
)
if err != nil {
fmt.Println("unable to create an AWS session for the provided profile")
return
}
req, _ := http.NewRequest(http.MethodGet, "", nil)
req = req.WithContext(ctx)
signer := v4.NewSigner(cfg.Credentials)
_, err = signer.Sign(req, nil, "execute-api", cfg.Region, time.Now())
if err != nil {
fmt.Printf("failed to sign request: (%v)\n", err)
return
}
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("failed to call remote service: (%v)\n", err)
return
}
defer res.Body.Close()
if res.StatusCode != 200 {
fmt.Printf("service returned a status not 200: (%d)\n", res.StatusCode)
return
}
The first argument to request.New is aws.Config, where you can send credentials.
https://github.com/aws/aws-sdk-go/blob/master/aws/request/request.go#L99
https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
There are multiple ways to create credentials object: https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html
For example using static values:
creds:= credentials.NewStaticCredentials("AKID", "SECRET_KEY", "TOKEN")
req := request.New(aws.Config{Credentials: creds}, ...)
I'm pretty new to go myself (3rd day learning go) but from watching the video you posted with the S3 example and reading through the source code (for the s3 service and request module) here is my understanding (which I'm hoping helps).
If you look at the code for the s3.New() function aws-sdk-go/service/s3/service.go
func New(p client.ConfigProvider, cfgs ...*aws.Config) *S3 {
c := p.ClientConfig(EndpointsID, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, .SigningName) }
As opposed to request.New() function aws-sdk-go/aws/request/request.go
func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request { ...
As you can see in the s3 scenario the *aws.Config struct is a pointer, and so is probably initialized / populated elsewhere. As opposed to the request function where the aws.Config is a parameter. So I am guessing the request module is probably a very low level module which doesn't get the shared credentials automatically.
Now, seeing as you will be interacting with API gateway I had a look at that service specifically to see if there was something similar. I looked at aws-sdk-go/service/apigateway/service.go
func New(p client.ConfigProvider, cfgs ...*aws.Config) *APIGateway {
c := p.ClientConfig(EndpointsID, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName) }...
Which looks pretty much the same as the s3 client, so perhaps try using that and see how you go?

One request - Two responses. Which end is creating the problem (Front / Back)

Problem:
When I call a request from iOS swift based app, then the server is responding two responses.
Inputs:
In my request, am sending some user values including one base64 image string. I already ensure that my app is calling request only one time.
Outputs:
When we opened the server log, it printed two set of request and response. But, difference that the first one is not having base64 image string and second one is having it. Thats why am receiving two different responses.
Questions:
What end is causing this problem - Front / back end?
Note:
I given front code below but I can’t provide back end code.
let task = urlSession.dataTask(with: urlRequest, completionHandler: {
(data, response, error) in
if error != nil
{
print("Error ==",error!.localizedDescription);
onFailure(error!.localizedDescription)
}
else
{
let httpResponse = response as! HTTPURLResponse
let statusCode = httpResponse.statusCode
// For some critical cases:
//print("Status code: ", statusCode)
//print("http Response: ", httpResponse)
// JSON serialize
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
print("Server Response == ",jsonResponse)
onSuccess(statusCode, jsonResponse)
}
catch
{
onFailure("JSON Parser Error")
}
}
})`

Proper testing of http routes in go

I have 5 end points which have methods such as GET, POST, and DELETE to test. I wrote test cases using the go's in built testing package. I'm worried that I'm missing some cases which are not striking to my mind.I have posted in code review for my test case to be reviewed but I didn't get any response. I have also followed this post Testing HTTP routes in golang. All these test cases are checking for the response codes.
The problem is that, most of my test cases follow similar pattern where I post data in different formats and checking the response codes. I strongly feel like I'm missing something that will break my API when I push it to prod. I need some insight on testing these routes so that I can be confident to push the api to prod.
main_test.go
func TestSigHandler(t *testing.T){
test_cases := []string{"2021205"}
// GET Testing
for _, method := range test_cases{
usersUrl = fmt.Sprintf("%s/1/sig/id/%s", server.URL, method) //Grab the address for the API endpoint
request, err := http.NewRequest("GET", usersUrl, nil)
res, err := http.DefaultClient.Do(request)
if err != nil {
t.Error(err) //Something is wrong while sending request
}
if res.StatusCode != 200 {
t.Errorf("Something went wrong : ", res.StatusCode) //Uh-oh this means our test failed
}
}
// POST Testing
sig := []byte( `{
"raw": "a new sig"
}`)
usersUrl = fmt.Sprintf("%s/1/sig/id/2021205", server.URL) //Grab the address for the API endpoint
request, err := http.NewRequest("POST", usersUrl, bytes.NewBuffer(sig))
if err != nil{
t.Error(err)
}
request.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(request)
if err != nil {
t.Error(err) //Something is wrong while sending request
}
if res.StatusCode != 200 {
t.Errorf(" Something Went Wrong: ", res.StatusCode) //Uh-oh this means our test failed
}
// DELETE Testing
sigs_delete_cases := []string{ "1000345"}
for _, sig_to_be_deleted := range sigs_delete_cases{
usersUrl = fmt.Sprintf("%s/1/sig/id/%s", server.URL, sig_to_be_deleted) //Grab the address for the API endpoint
request, err := http.NewRequest("DELETE", usersUrl, nil)
res, err := http.DefaultClient.Do(request)
if err != nil {
t.Error(err) //Something is wrong while sending request
}
if res.StatusCode != 200 {
t.Errorf("Tried to delete a reserved Id : ", res.StatusCode) //Uh-oh this means our test failed
}
}
}
I like to do this way:
Establish Continuous Integration. If your project is Open Source, you may use services like Travis CI - it has very easy installation. This helps you to see how changes affect code.
Set code test coverage. It allows you to see what source code lines are covered with tests and what are not and where very possible bugs will emerge. Of course, code coverage tool is not a panacea. And if line was checked it doesn't mean it is absolutely ok, and it will not fail with other input. But it helps much to maintain good code and look for bugs. For open source you may use coveralls.io. There's a special goveralls plugin for it.
To help the problem above you may use so-called Fuzzy testing - exploratory tests with random input to find a root cause. There're standard https://golang.org/pkg/testing/quick/ and non-standard packages https://github.com/dvyukov/go-fuzz.
Then I experiment with tests, they are both positive and negative. I try check situation with errors, timeouts, incorrect replies.
For my tests I've used as usual client http so httptest package.

Correctly using httptest to mock responses

I have something that looks like so:
func (client *MyCustomClient) CheckURL(url string, json_response *MyCustomResponseStruct) bool {
r, err = http.Get(url)
if err != nil {
return false
}
defer r.Body.Close()
.... do stuff with json_response
And in my test, I have the following:
func TestCheckURL(t *test.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
fmt.Fprintln(w, `{"status": "success"}`)
}))
defer ts.Close()
json_response := new(MyCustomResponseStruct)
client := NewMyCustomClient() // returns instance of MyCustomClient
done := client.CheckURL("test.com", json_response)
However, it does not appear as if the HTTP test server is working and that it actually goes out to test.com, as evidenced by the log output:
Get http:/test.com: dial tcp X.Y.Z.A: i/o timeout
My question is how to properly use the httptest package to mock out this request... I read through the docs and this helpful SO Answer but I'm still stuck.
Your client only calls the URL you provided as the first argument to the CheckURL method. Give your client the URL of your test server:
done := client.CheckURL(ts.URL, json_response)