แสดงบทความที่มีป้ายกำกับ golang แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ golang แสดงบทความทั้งหมด

วันอังคารที่ 23 กุมภาพันธ์ พ.ศ. 2559

[go lang] Unit test with gorilla/mux

นั่งงมทำ unit test บน go กับ lib gorilla/mux มาครึ่งวัน เพราะเวลายิง test แล้ว มันไม่สามารถดึงตัวแปรได้

โดยปรกติเวลาเราสร้าง router บน gorilla/mux เราจะสร้าง


rtr.HandleFunc("/v1/exp/{UUID}", GetProductExp).Methods("GET")
เวลาดึงค่าก็ใช้
params := mux.Vars(r)
fmt.Println(params["UUID"]

แต่เวลาเราทำ unit test มันจะทำให้ function handle ของเราดึง var ออกมาไม่ได้เพราะถ้าเขียน unit test ปรกติจะเขียนประมาณ

func TestGetProductExp(t *testing.T) {
        fmt.Println("Testing GetProductExp")
        request, _ := http.NewRequest("GET", "/v1/exp/21a56778-6aad-4d90-aa16-6b5748a9f717", nil)
        response := httptest.NewRecorder()
        GetProductExp(response, request)
        fmt.Println(response.Body)
}

แต่เมื่อสั่ง go test ไปแล้วผลปรากฏว่า funcHandle ที่เขียนไว้ดึงค่าไม่ได้ทั้งๆที่ลอง run program ดูจริงๆแล้วลองยิง req ดูจริงๆแล้วมันก็อ่านค่าตัวแปรได้
ตอนหาข้อมูลมีคนเจอปัญหาเหมือนผมด้วย http://mrgossett.com/post/mux-vars-problem/
นั่งหาอ่านอยู่เกือบครึ่งวันกว่าจะได้คำตอบว่า lib gorilla/mux มันแปลงค่าตัวแปรให้มาอยู่ในรูป map[string]string ทำให้เวลายิงผ่าน test มันแปลงข้อมูลผิด ถ้าจะทำ unit test กับ gorilla/mux ให้เขียน test ประมาณนี้

func TestGetProductExp(t *testing.T) {
        fmt.Println("Testing GetProductExp")
        request, _ := http.NewRequest("GET", "/v1/exp/21a56778-6aad-4d90-aa16-6b5748a9f717", nil)
        response := httptest.NewRecorder()
        m := mux.NewRouter()
        m.HandleFunc("/v1/exp/{UUID}", GetProductExp)
        m.ServeHTTP(response, request)
        fmt.Println(response.Body)
}

วันพุธที่ 24 มิถุนายน พ.ศ. 2558

Download & Read Manga via Golang

เวลาอ่านกาตูน Online ผมอ่านอยู่ไม่กี่เว็บผมเลยเขียน program สำหรับ ดูด กาตูนเหล่านั้นมาลงไว้ที่เครื่องแล้วค่อยอ่านทีหลัง https://github.com/neverlock/Mangafox
แต่ดูดมาก็เป็นภาระ ต้องมาหาโปรแกรมเปิดอ่าน ปรกติแล้วผมใช้ Ehon ผมว่าตัวนี้เป็นตัวอ่าน manga บน MAC ที่ดูดีที่สุดละ แต่บางทีก็ขี้เกียจเปิดมันเพราะต้อง Import Dir รูปเข้าไปให้มันก่อน



จากความรำคาญเลยเขียน golang web service สำหรับอ่านกาตูนอีกตัว โดย hack net/http ใน function ที่ทำงาน serv static file เอามาแสดงผลให้แสดง tag img เพื่อ แสดงรูปซะเลย

ใน function main เขียนแค่ บรรทัดเดียว



package main

import (
        "log"
        "github.com/neverlock/manga_net/http"
)


func main() {
        log.Println("Service at :8080")
        log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("./"))))
}

สั่ง go get github.com/neverlock/manga_net/http
จากนั้นสั่ง go build
แล้วเอา binary ที่ได้ไปวางไว้ใน Dir ที่ load manga มาไว้แล้วสั่ง run แล้วเปิดดูที่ web browser ได้เลย

การแสดงผลจะเหมือนกับการ serv static file แต่ถ้าใน Dir มีรูปมันจะแสดงรูปแทนที่จะเป็น link 



วันอังคารที่ 2 มิถุนายน พ.ศ. 2558

golang SOCKS5 server

ไหนๆวันนี้ก็เขียนเรื่อง go SOCKS client ไปแล้วเขียนเรืองทำ SOCKS server ด้วย go ไปเลยดีกว่าเพราะเห็น Bright มากด like ให้ด้วยเห็นวันก่อนถามเรื่องทำ SOCKS server นิ

package main

import "github.com/armon/go-socks5"

func main(){
conf := &socks5.Config{}
server, err := socks5.New(conf)
if err != nil {
  panic(err)
}

// Create SOCKS5 proxy on localhost port 8000
if err := server.ListenAndServe("tcp", "127.0.0.1:8000"); err != nil {
  panic(err)
        }
}

lib ตัวนี้เป็น SOCKS5 อย่างเดียวเวลา connect ให้ตั้ง type เป็น SOCKS5 ด้วยนะเวลาจะทดสอบ


ตามรูปตัวอย่างบรรทัดด้านบนเป็นการลองส่ง  request จาก client ที่ set SOCKS 4 มาจะเห็นว่า connect ไม่ได้ ถ้า set ถูกจะเห็นตามบรรทัดล่างๆคือ มีการส่งข้อมูลไปกลับระหว่าตัว client กับ target 
แค่นี้ยังไม่พอเรามา hack เพิ่มอีกนิดหน่อยดีกว่า จริงๆผมอยาก print log มากกว่านี้แต่ ไปอ่านdoc มันแล้วไม่มี function พวกนี้เลย เรามาเขียน ความสามารถที่เราอยากได้เพิ่มให้กับ lib มันเลยดีกว่า
โดยเข้าไปแก้ file ที่อยู่ใน path src/github.com/armon/go-socks5/request.go
หรือถ้าต้องการ monitor อะไรเพิ่มเติมก็ไปเขียน hook เพิ่มเอาได้เลย


func (s *Server) handleConnect(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error {
        // Check if this is allowed
        client := conn.RemoteAddr().(*net.TCPAddr)
        if !s.config.Rules.AllowConnect(realDest.IP, realDest.Port, client.IP, client.Port) {
                if err := sendReply(conn, ruleFailure, nil); err != nil {
                        return fmt.Errorf("Failed to send reply: %v", err)
                }
                return fmt.Errorf("Connect to %v blocked by rules", dest)
        }

        // Attempt to connect
        addr := net.TCPAddr{IP: realDest.IP, Port: realDest.Port}
        target, err := net.DialTCP("tcp", nil, &addr)
        if err != nil {
                msg := err.Error()
                resp := hostUnreachable
                if strings.Contains(msg, "refused") {
                        resp = connectionRefused
                } else if strings.Contains(msg, "network is unreachable") {
                        resp = networkUnreachable
                }
                if err := sendReply(conn, resp, nil); err != nil {
                        return fmt.Errorf("Failed to send reply: %v", err)
                }
                return fmt.Errorf("Connect to %v failed: %v", dest, err)
        }
        defer target.Close()

        // Send success
        local := target.LocalAddr().(*net.TCPAddr)
        bind := AddrSpec{IP: local.IP, Port: local.Port}
        if err := sendReply(conn, successReply, &bind); err != nil {
                return fmt.Errorf("Failed to send reply: %v", err)
        }

        // Start proxying
        errCh := make(chan error, 2)
        go proxy("target", target, bufConn, errCh,realDest.IP)
        go proxy("client", conn, target, errCh,local.IP)

        // Wait
        select {
        case e := <-errCh:
                return e
        }
}

function handleConnection เพิ่งตรงให้ go routine เรียก function โดยเพิ่มตัวแปร IP เข้าไปด้วย


func proxy(name string, dst io.Writer, src io.Reader, errCh chan error,ip1 net.IP) {
        // Copy
        n, err := io.Copy(dst, src)

        // Log, and sleep. This is jank but allows the otherside
        // to finish a pending copy
        log.Printf("[DEBUG] socks: Copied %d bytes to %s[%v]", n, name,ip1)
        time.Sleep(10 * time.Millisecond)

        // Send any errors
        errCh <-err
}
และที่ function proxy ให้รับตัวแปรเพิ่มอีก 1 ตัวและ print log IP ออกมาแสดง
จะได้ผลตามนี้


แค่ code ไม่กี่บรรทัดก็ได้ SOCKS server แล้ว

gohls SOCKS client

วันนี้จะโหลดหนังกับ HLS ผ่าน tsocks บน linux แต่ tsocks ดันทำงานไม่ได้ บน ubuntu 15.04 ไม่รู้เป็นเพราะอะไร สั่ง tsocks command แล้วมันไม่ยอมออก socks เลยแก้ปัญหาด้วยการเขียนให้ตัว โหลด HLS มันใช้ socks ได้ด้วยตัวมันเองซะเลย

ตัว HLS download ใช้ตัวเดิมคือ https://github.com/kz26/gohls

ตัว SOCKS lib ใช้ https://github.com/hailiang/socks

แก้ code main.go ของ gohls ตามนี้



package main

import "flag"
import "fmt"
import "io"
import "net/http"
import "net/url"
import "log"
import "os"
import "time"
import "github.com/golang/groupcache/lru"
import "strings"
import "github.com/kz26/m3u8"
import "h12.me/socks"

const VERSION = "1.0.5"

var USER_AGENT string

var dialSocksProxy = socks.DialSocksProxy(socks.SOCKS5, "127.0.0.1:1080")
var tr = &http.Transport{Dial: dialSocksProxy}
var client = &http.Client{Transport: tr}

func doRequest(c *http.Client, req *http.Request) (*http.Response, error) {
        req.Header.Set("User-Agent", USER_AGENT)
        resp, err := c.Do(req)
        return resp, err
}
แค่นี้เราก็สามารถใช้ socks ในโปรแกรมเราได้แล้ว
ส่วนสำคัญคือแก้ code เพิ่ม 4 บรรทัดนี้

import "h12.me/socks"
var dialSocksProxy = socks.DialSocksProxy(socks.SOCKS5, "127.0.0.1:1080")
var tr = &http.Transport{Dial: dialSocksProxy}
var client = &http.Client{Transport: tr}

วันพฤหัสบดีที่ 21 พฤษภาคม พ.ศ. 2558

วิธีใช้ mongodb3.0 กับ golang

ปรกติแล้วเวลาเขียน  go ต่อกับ mongodb ผมจะใช้ lib ตัวนี้คือ

        "gopkg.in/mgo.v2"

        "gopkg.in/mgo.v2/bson"

วันนี้ลองใช้กับ mongodb 3.0.3 มัน auth ไม่ผ่านขึ้นมาประมาณนี้

server returned error on SASL authentication step: Authentication failed.

ไปค้นๆดูเจอปัญหาและวิธีแก้ดังนี้
ปัญหาเกิดจากรูปแบบการ auth ของ mongodb 3.0 เปลี่ยนจาก version เก่าใน ตัวแปร authSchema  ใช้เป็น version 5 ตัว auth ของ lib ที่ใช้อยู่จะใช้ได้กับ version 3 โดยเราสามารถแก้ได้ดังนี้

login mongo โดยใช้ admin user หรือไปเป็น noauth ใน config file แล้ว start mongodb แล้วค่อยสั่ง mongo

> db.system.version.find()
{ "_id" : "authSchema", "currentVersion" : 5 }

จะเห็นว่าเป็น version 5 อยู่ให้ใช้คำสั่งดังนี้

> db.system.version.update({ "_id" : "authSchema"},{$set: {"currentVersion" : 3} })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.system.version.find()
{ "_id" : "authSchema", "currentVersion" : 3 }

แค่นี้เราก็สามารถใช้ golang กับ mongodb version 3 ได้แล้ว
ใครแก้ config file เป็น noauth ก็อย่าลืมแก้กลับก่อนด้วยหละเดี๋ยวจะหาว่าไม่เตือน

วันอาทิตย์ที่ 22 มีนาคม พ.ศ. 2558

Simple Form post with Golang

วันนี้ขอนำเสนอวิธีการ ส่ง HTTP request เรื่องการ post form การ post form นั้นจะมีการส่งค่าตรงแปรต่างๆเข้าไปด้วย ในภาษา Go นั้น package net/http มี function PostForm ให้เราใช้ได้ง่ายๆเลย
ตัวอย่างง่ายๆตามนี้เลย


package main

import ("net/http"
        "log"
        "net/url"
        "fmt"
        )

func main()  {

 postUrl := "https://www.somesite.com/login"

 values := make(url.Values)

 values.Set("user", "neverlock")
 values.Set("pwd", "password")

 // Submit form
 resp, err := http.PostForm(postUrl, values)
 if err != nil {
         log.Fatal(err)
 }
 fmt.Printf("[%d] %s\n",i,resp.Status)
 defer resp.Body.Close()

}

สำหรับวิธีง่ายๆในการที่จะหาว่า มีค่าอะไรบ้างที่จะต้องส่งไปในการ post form แต่ละครั้งก็ให้ใช้ chrome หรือ firefox แล้วเปิด developer tool ขึ้นมาจากนั้น post form ไปเลยเพื่อดูว่ามันส่งอะไรมั่งจะได้ไม่ต้องไปเสียเวลานั่งไล่ code html ดูในส่วนของ Form Data ได้เลยว่า formpost นั้นๆส่งค่าอะไรไปบ้าง


วันพฤหัสบดีที่ 12 มีนาคม พ.ศ. 2558

Free geoip api service

เมื่อกี้ไปเจอคนในกลุ่ม go Thailand dev post เกี่ยวกับ go project ที่ปล่อย api geoip เห็นน่าสนใจดีเลยลองตั้ง service ดูใช้งานง่ายดีไม่มีอะไรยุ่งยากร้องขอ output ได้ 3 แบบคือ json,xml และ csv 

http://geoip.conf.in.th:8080/json/
http://geoip.conf.in.th:8080/xml/
http://geoip.conf.in.th:8080/csv/

วิธีการด้านบนจะเป็นการถาม IP ตัวเองแต่ถ้าอยากจะระบุหา IP อื่นก็ใช้แบบนี้

http://geoip.conf.in.th:8080/json/8.8.8.8

ใครสนใจ project นี้ตามไปอ่าน code ได้ที่ 

https://github.com/fiorix/freegeoip

สำหรับความแม่นยำนั้นก็อยู่ในระดับของฟรี ผมเข้าไปตรวจสอบดูละประเทศไทยความแม่นยำในระดับ 50KM อยู่ที่ 28% เอง (ตัว GeoIP2 City ที่เป็น 60% นั้นมันของเสียเงินซื้อครับ )

 

ถ้าเป็น IP เมกาจะค่อนข้างแม่นคือประมาณ 71%


วันอังคารที่ 10 มีนาคม พ.ศ. 2558

Golang simple web service

Go lang เป็นภาษาที่เหมาะสำหรับทำ web service มาก code ที่สั่ง start web server แค่ บรรทัดเดียวก็ทำได้ละ (บรรทัดอื่นๆเป็นการ controll route ที่จะวิ่งเข้าที่ server)
จริงๆแล้วการกำหนด route สามารถเขียนเองใน go โดยไม่ต้อง import อะไรพิเศษได้แต่ส่วนตัวแล้วผมชอบ lib ของ github.com/gorilla/mux code นี้จะเป็น code ง่ายๆที่ return ค่า "Hello world" ออกมาถ้าเราเข้าเว็บ http://localhost:8080/hello


go get github.com/gorilla/mux


package main

import (
        "fmt"
        "net/http"
        "github.com/gorilla/mux"
)

func main() {
        rtr := mux.NewRouter()
        rtr.HandleFunc("/hello",sayHi).Methods("GET")
        http.Handle("/", rtr)
        bind := ":8080"
        fmt.Printf("listening on %s...\n", bind)
        http.ListenAndServe(bind, nil)

}

func sayHi (w http.ResponseWriter, r *http.Request){
        str:="Hello world"
        w.Write([]byte (str))
}

วันอาทิตย์ที่ 8 มีนาคม พ.ศ. 2558

Golang Concat variables

ช่วงนี้กำลังหัดเขียนภาษา Go รู้สึกถูกจริตกับภาษานี้มากเพราะมันให้บรรยากาศเหมือนเขียน C และภาษา script แต่ทำอะไรสมัยใหม่ได้โดยไม่ต้องพัฒนาอะไรเองเพิ่งมากนักแถมยังกิน resource น้อยและทำงานได้ดีอีกด้วย



โดยปรกติภาษาใหม่ๆหน่อยมันจะมีวิธี Concat variables กับ Strings ได้ง่ายๆแต่ของ golang จะใช้ Sprintf ช่วยตามตัวอย่างนี้


query := fmt.Sprintf("Uid=%s&fName=%s&lName=%s&searchType=null",ID,FN,LN)