Trying to seperate out each part of the script into functions to use the output later on.
Cannot get this part to work when trying to pass in instances to the printVolumesInfo function.
[]InstanceBlockDeviceMapping is part of the Instance struct but I am unsure what to use as an input for the function.
`
package main
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ec2"
)
var client *ec2.Client
func init() {
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
panic("configuration error, " + err.Error())
}
client = ec2.NewFromConfig(cfg)
}
func printVolumesInfo(volumes []ec2.InstanceBlockDeviceMapping) {
for _, b := range volumes {
fmt.Println(" " + *b.DeviceName)
fmt.Println(" " + *b.Ebs.VolumeId)
}
}
func main() {
parms := &ec2.DescribeInstancesInput{}
result, err := client.DescribeInstances(context.TODO(), parms)
if err != nil {
fmt.Println("Error calling ec2: ", err)
return
}
for _, r := range result.Reservations {
fmt.Println("Reservation ID: " + *r.ReservationId)
fmt.Println("Instance IDs:")
for _, i := range r.Instances {
fmt.Println(" " + *i.InstanceId)
printVolumesInfo(i.InstanceBlockDeviceMapping)
}
}
}
`
Error received:
./main.go:74:37: undefined: ec2.InstanceBlockDeviceMapping
Tried to use different parameters including []InstanceBlockDeviceMapping and BlockDeviceMapping. Also, used ec2 and client for the values as well.
Check the documentation: https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/ec2/types#Instance
The field is called BlockDeviceMappings. And the type InstanceBlockDeviceMapping is in the package github.com/aws/aws-sdk-go-v2/service/ec2/types, not in the ec2 package.
Add github.com/aws/aws-sdk-go-v2/service/ec2/types` to your imports
Change argument type of function printVolumes to volumes []ec2.InstanceBlockDeviceMapping
Call the function as printVolumesInfo(i.BlockDeviceMappings)
I'm playing around with the ICMP raw socket of Golang. I'd like to read the TTL which is part the control message returned by ReadFrom(buffer).
Weirdly this value is always nil, is there something I'm missing.
Please find below my playground code:
package main
import (
"fmt"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
func main() {
c, _ := icmp.ListenPacket("ip4:icmp", "")
rb := make([]byte, 1500)
for true {
n, cm, peer, _ := c.IPv4PacketConn().ReadFrom(rb)
rm, _ := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), rb[:n])
switch rm.Type {
case ipv4.ICMPTypeEchoReply:
{
fmt.Printf("received answer from %s\n", peer)
if cm != nil {
println(cm.TTL)
} else {
println("empty control message")
}
}
default:
}
}
}
Finally, I found out what was missing.
Before reading, it is required to set IP socket options.
In my case, I was interested in TTL, so:
_ = c.IPv4PacketConn().SetControlMessage(ipv4.FlagTTL, true)
I'm trying to write a tiny application in Go that can send an HTTP request to all IP addresses in hopes to find a specific content. The issue is that the application seems to crash in a very peculiar way when the call is executed asynchronously.
ip/validator.go
package ip
import (
"io/ioutil"
"net/http"
"regexp"
"time"
)
type ipValidator struct {
httpClient http.Client
path string
exp *regexp.Regexp
confirmationChannel *chan string
}
func (this *ipValidator) validateUrl(url string) bool {
response, err := this.httpClient.Get(url)
if err != nil {
return false
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return false
}
bodyBytes, _ := ioutil.ReadAll(response.Body)
result := this.exp.Match(bodyBytes)
if result && this.confirmationChannel != nil {
*this.confirmationChannel <- url
}
return result
}
func (this *ipValidator) ValidateIp(addr ip) bool {
httpResult := this.validateUrl("http://" + addr.ToString() + this.path)
httpsResult := this.validateUrl("https://" + addr.ToString() + this.path)
return httpResult || httpsResult
}
func (this *ipValidator) GetSuccessChannel() *chan string {
return this.confirmationChannel
}
func NewIpValidadtor(path string, exp *regexp.Regexp) ipValidator {
return newValidator(path, exp, nil)
}
func NewAsyncIpValidator(path string, exp *regexp.Regexp) ipValidator {
c := make(chan string)
return newValidator(path, exp, &c)
}
func newValidator(path string, exp *regexp.Regexp, c *chan string) ipValidator {
httpClient := http.Client{
Timeout: time.Second * 2,
}
return ipValidator{httpClient, path, exp, c}
}
main.go
package main
import (
"./ip"
"fmt"
"os"
"regexp"
)
func processOutput(c *chan string) {
for true {
url := <- *c
fmt.Println(url)
}
}
func main() {
args := os.Args[1:]
fmt.Printf("path: %s regex: %s", args[0], args[1])
regexp, regexpError := regexp.Compile(args[1])
if regexpError != nil {
fmt.Println("The provided regexp is not valid")
return
}
currentIp, _ := ip.NewIp("172.217.22.174")
validator := ip.NewAsyncIpValidator(args[0], regexp)
successChannel := validator.GetSuccessChannel()
go processOutput(successChannel)
for currentIp.HasMore() {
go validator.ValidateIp(currentIp)
currentIp = currentIp.Increment()
}
}
Note the line that says go validator.ValidateIp(currentIp) in main.go. Should I remove the word "go" to execute everything within the main routine, the code works as expected -> it sends requests to IP addresses starting 172.217.22.174 and should one of them return a legitimate result that matches the regexp that the ipValidator was initialized with, the URL is passed to the channel and the value is printed out by processOutput function from main.go. The issue is that simply adding go in front of validator.ValidateIp(currentIp) breaks that functionality. In fact, according to the debugger, I never seem to go past the line that says response, err := this.httpClient.Get(url) in validator.go.
The struggle is real. Should I decide to scan the whole internet, there's 256^4 IP addresses to go through. It will take years, unless I find a way to split the process into multiple routines.
Hi I have two problems in the following Go Program .
1. I couldn't read the space seperated string using Scanf or Scanln.
So I have added a formatted string "%q" to read space seperated string using double quotes.
Is there an alternative to read string with spaces ?
package main
import
(
"fmt"
"strings"
)
type details struct{
DataType string
Table string
}
func main(){
dt := details{}
fmt.Println("Enter the DataType")
fmt.Scanf("%q" ,&dt.DataType )
for strings.TrimSpace(dt.DataType) == "" {
fmt.Println("Enter the DataType")
fmt.Scanln(&dt.DataType)
}
//fmt.Println(dt.DataType)
fmt.Println("Enter the Table")
fmt.Scanln(&dt.Table)
for strings.TrimSpace(dt.Table) == "" {
fmt.Println("Enter a valid Table name ")
fmt.Scanln(&dt.Table)
}
}
The Console output is as follows ,
VenKats-MacBook-Air:ColumnCreator venkat$ go run test.go
Enter the DataType
"rid bigint not null"
Enter the Table
Enter a valid Table name
The Second problem is why does the control flow went to the second for loop without waiting for the user input . Does the Scanf with "%q" returned a carraige return .
Any help would be greatly appreciated
Perhaps something like this..
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
type details struct {
DataType string
Table string
}
func main() {
dt := details{}
cin := bufio.NewReader(os.Stdin)
for {
fmt.Println("Enter the DataType")
text, err := cin.ReadString('\n') // reads entire string up until the /n which is the newline deliminator
if strings.TrimSpace(text) == "" { // check to see if the input is empty
continue
}
if err == nil { // if the input is not empty then the control got this far and now we just have to check for error, assign the data, and break out of the loop .. repeat for the second input. If this is going to be something you do alot refactor the input section.
dt.DataType = text
break
} else {
fmt.Printf("An error as occured: %s\n", err.Error())
}
}
for {
fmt.Println("Enter the Table")
text, err := cin.ReadString('\n')
if strings.TrimSpace(text) == "" {
continue
}
if err == nil {
dt.Table = text
break
} else {
fmt.Printf("An error as occured: %s\n", err.Error())
}
}
fmt.Printf("%+v\n", dt)
return
}
Example of refactored code:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
type details struct {
DataType string
Table string
}
func getInput(message string, reader bufio.Reader) (input string) {
for {
fmt.Println(message)
input, err := reader.ReadString('\n')
if strings.TrimSpace(input) == "" {
continue
}
if err == nil {
break
} else {
fmt.Printf("An error as occured: %s\n", err.Error())
}
}
return
}
func main() {
dt := details{}
cin := bufio.NewReader(os.Stdin)
t := getInput("Enter the DataType", *cin)
dt.DataType = t
t = getInput("Enter the Table", *cin)
dt.Table = t
fmt.Printf("Seeing what my data looks like %+v\n", dt)
return
}
Is there a native way for inplace url parameters in native Go?
For Example, if I have a URL: http://localhost:8080/blob/123/test I want to use this URL as /blob/{id}/test.
This is not a question about finding go libraries. I am starting with the basic question, does go itself provide a basic facility to do this natively.
There is no built in simple way to do this, however, it is not hard to do.
This is how I do it, without adding a particular library. It is placed in a function so that you can invoke a simple getCode() function within your request handler.
Basically you just split the r.URL.Path into parts, and then analyse the parts.
// Extract a code from a URL. Return the default code if code
// is missing or code is not a valid number.
func getCode(r *http.Request, defaultCode int) (int, string) {
p := strings.Split(r.URL.Path, "/")
if len(p) == 1 {
return defaultCode, p[0]
} else if len(p) > 1 {
code, err := strconv.Atoi(p[0])
if err == nil {
return code, p[1]
} else {
return defaultCode, p[1]
}
} else {
return defaultCode, ""
}
}
Well, without external libraries you can't, but may I recommend two excellent ones:
httprouter - https://github.com/julienschmidt/httprouter - is extremely fast and very lightweight. It's faster than the standard library's router, and it creates 0 allocations per call, which is great in a GCed language.
Gorilla Mux - http://www.gorillatoolkit.org/pkg/mux -
Very popular, nice interface, nice community.
Example usage of httprouter:
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}
func main() {
router := httprouter.New()
router.GET("/hello/:name", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}
What about trying using regex, and find a named group in your url, like playground:
package main
import (
"fmt"
"net/url"
"regexp"
)
var myExp = regexp.MustCompile(`/blob/(?P<id>\d+)/test`) // use (?P<id>[a-zA-Z]+) if the id is alphapatic
func main() {
s := "http://localhost:8080/blob/123/test"
u, err := url.Parse(s)
if err != nil {
panic(err)
}
fmt.Println(u.Path)
match := myExp.FindStringSubmatch(s) // or match := myExp.FindStringSubmatch(u.Path)
result := make(map[string]string)
for i, name := range myExp.SubexpNames() {
if i != 0 && name != "" {
result[name] = match[i]
}
}
fmt.Printf("id: %s\n", result["id"])
}
output
/blob/123/test
id: 123
Below full code to use it with url, that is receiving http://localhost:8000/hello/John/58 and returning http://localhost:8000/hello/John/58:
package main
import (
"fmt"
"net/http"
"regexp"
"strconv"
)
var helloExp = regexp.MustCompile(`/hello/(?P<name>[a-zA-Z]+)/(?P<age>\d+)`)
func hello(w http.ResponseWriter, req *http.Request) {
match := helloExp.FindStringSubmatch(req.URL.Path)
if len(match) > 0 {
result := make(map[string]string)
for i, name := range helloExp.SubexpNames() {
if i != 0 && name != "" {
result[name] = match[i]
}
}
if _, err := strconv.Atoi(result["age"]); err == nil {
fmt.Fprintf(w, "Hello, %v year old named %s!", result["age"], result["name"])
} else {
fmt.Fprintf(w, "Sorry, not accepted age!")
}
} else {
fmt.Fprintf(w, "Wrong url\n")
}
}
func main() {
http.HandleFunc("/hello/", hello)
http.ListenAndServe(":8090", nil)
}
How about writing your own url generator (extend net/url a little bit) as below.
// --- This is how does it work like --- //
url, _ := rest.NewURLGen("http", "stack.over.flow", "1234").
Pattern(foo/:foo_id/bar/:bar_id).
ParamQuery("foo_id", "abc").
ParamQuery("bar_id", "xyz").
ParamQuery("page", "1").
ParamQuery("offset", "5").
Do()
log.Printf("url: %s", url)
// url: http://stack.over.flow:1234/foo/abc/bar/xyz?page=1&offset=5
// --- Your own url generator would be like below --- //
package rest
import (
"log"
"net/url"
"strings"
"straas.io/base/errors"
"github.com/jinzhu/copier"
)
// URLGen generates request URL
type URLGen struct {
url.URL
pattern string
paramPath map[string]string
paramQuery map[string]string
}
// NewURLGen new a URLGen
func NewURLGen(scheme, host, port string) *URLGen {
h := host
if port != "" {
h += ":" + port
}
ug := URLGen{}
ug.Scheme = scheme
ug.Host = h
ug.paramPath = make(map[string]string)
ug.paramQuery = make(map[string]string)
return &ug
}
// Clone return copied self
func (u *URLGen) Clone() *URLGen {
cloned := &URLGen{}
cloned.paramPath = make(map[string]string)
cloned.paramQuery = make(map[string]string)
err := copier.Copy(cloned, u)
if err != nil {
log.Panic(err)
}
return cloned
}
// Pattern sets path pattern with placeholder (format `:<holder_name>`)
func (u *URLGen) Pattern(pattern string) *URLGen {
u.pattern = pattern
return u
}
// ParamPath builds path part of URL
func (u *URLGen) ParamPath(key, value string) *URLGen {
u.paramPath[key] = value
return u
}
// ParamQuery builds query part of URL
func (u *URLGen) ParamQuery(key, value string) *URLGen {
u.paramQuery[key] = value
return u
}
// Do returns final URL result.
// The result URL string is possible not escaped correctly.
// This is input for `gorequest`, `gorequest` will handle URL escape.
func (u *URLGen) Do() (string, error) {
err := u.buildPath()
if err != nil {
return "", err
}
u.buildQuery()
return u.String(), nil
}
func (u *URLGen) buildPath() error {
r := []string{}
p := strings.Split(u.pattern, "/")
for i := range p {
part := p[i]
if strings.Contains(part, ":") {
key := strings.TrimPrefix(p[i], ":")
if val, ok := u.paramPath[key]; ok {
r = append(r, val)
} else {
if i != len(p)-1 {
// if placeholder at the end of pattern, it could be not provided
return errors.Errorf("placeholder[%s] not provided", key)
}
}
continue
}
r = append(r, part)
}
u.Path = strings.Join(r, "/")
return nil
}
func (u *URLGen) buildQuery() {
q := u.URL.Query()
for k, v := range u.paramQuery {
q.Set(k, v)
}
u.RawQuery = q.Encode()
}
With net/http the following would trigger when calling localhost:8080/blob/123/test
http.HandleFunc("/blob/", yourHandlerFunction)
Then inside yourHandlerFunction, manually parse r.URL.Path to find 123.
Note that if you don't add a trailing / it won't work. The following would only trigger when calling localhost:8080/blob:
http.HandleFunc("/blob", yourHandlerFunction)
As of 19-Sep-22, with go version 1.19, instance of http.request URL has a method called Query, which will return a map, which is a parsed query string.
func helloHandler(res http.ResponseWriter, req *http.Request) {
// when request URL is `http://localhost:3000/?first=hello&second=world`
fmt.Println(req.URL.Query()) // outputs , map[second:[world] first:[hello]]
res.Write([]byte("Hello World Web"))
}
No way without standard library. Why you don't want to try some library? I think its not so hard to use it, just go get bla bla bla
I use Beego. Its MVC style.
how about a simple utility function ?
func withURLParams(u url.URL, param, val string) url.URL{
u.Path = strings.ReplaceAll(u.Path, param, val)
return u
}
you can use it like this:
u, err := url.Parse("http://localhost:8080/blob/:id/test")
if err != nil {
return nil, err
}
u := withURLParams(u, ":id","123")
// now u.String() is http://localhost:8080/blob/123/test
If you need a framework and you think it will be slow because it's 'bigger' than a router or net/http, then you 're wrong.
Iris is the fastest go web framework that you will ever find, so far according to all benchmarks.
Install by
go get gopkg.in/kataras/iris.v6
Django templates goes easy with iris:
import (
"gopkg.in/kataras/iris.v6"
"gopkg.in/kataras/iris.v6/adaptors/httprouter"
"gopkg.in/kataras/iris.v6/adaptors/view" // <-----
)
func main() {
app := iris.New()
app.Adapt(iris.DevLogger())
app.Adapt(httprouter.New()) // you can choose gorillamux too
app.Adapt(view.Django("./templates", ".html")) // <-----
// RESOURCE: http://127.0.0.1:8080/hi
// METHOD: "GET"
app.Get("/hi", hi)
app.Listen(":8080")
}
func hi(ctx *iris.Context){
ctx.Render("hi.html", iris.Map{"Name": "iris"})
}