package curl2info import ( "fmt" "io/ioutil" "log" "net/http" "net/http/cookiejar" "net/url" "os" "regexp" "strconv" "strings" "474420502.top/eson/requests" ) // CURL 信息结构 type CURL struct { ParsedURL *url.URL Method string Header http.Header CookieJar http.CookieJar Cookies []*http.Cookie Body *requests.Body Auth *requests.BasicAuth Timeout int // second Insecure bool CallBack string } // NewCURL new 一个 curl 出来 func NewCURL() *CURL { u := &CURL{} u.Insecure = false u.Header = make(http.Header) u.CookieJar, _ = cookiejar.New(nil) u.Body = requests.NewBody() u.Timeout = 30 return u } func (curl *CURL) String() string { if curl != nil { return fmt.Sprintf("Method: %s\nParsedURL: %s\nHeader: %s\nCookie: %s", curl.Method, curl.ParsedURL.String(), curl.Header, curl.Cookies) } return "" } // CreateSession 创建Session func (curl *CURL) CreateSession() *requests.Session { ses := requests.NewSession() ses.SetHeader(curl.Header) ses.SetCookies(curl.ParsedURL, curl.Cookies) ses.SetConfig(requests.CRequestTimeout, curl.Timeout) if curl.Auth != nil { ses.SetConfig(requests.CBasicAuth, curl.Auth) } if curl.Insecure { ses.SetConfig(requests.CInsecure, curl.Insecure) } return ses } // CreateWorkflow 根据Session 创建Workflow func (curl *CURL) CreateWorkflow(ses *requests.Session) *requests.Workflow { var wf *requests.Workflow if ses == nil { ses = curl.CreateSession() } switch curl.Method { case "HEAD": wf = ses.Head(curl.ParsedURL.String()) case "GET": wf = ses.Get(curl.ParsedURL.String()) case "POST": wf = ses.Post(curl.ParsedURL.String()) case "PUT": wf = ses.Put(curl.ParsedURL.String()) case "PATCH": wf = ses.Patch(curl.ParsedURL.String()) case "OPTIONS": wf = ses.Options(curl.ParsedURL.String()) case "DELETE": wf = ses.Delete(curl.ParsedURL.String()) } wf.SetBody(curl.Body) return wf } func init() { optionTrie = NewTrie() oelist := []*optionExecute{ {"-H", 10, parseHeader, nil}, {"-X", 10, parseOptX, nil}, {"-A", 15, parseUserAgent, &extract{re: "^-A +(.+)", execute: extractData}}, {"-I", 15, parseOptI, nil}, {"-d", 10, parseBodyASCII, &extract{re: "^-d +(.+)", execute: extractData}}, {"-u", 15, parseUser, &extract{re: "^-u +(.+)", execute: extractData}}, {"-k", 15, parseInsecure, nil}, // Body {"--data", 10, parseBodyASCII, &extract{re: "--data +(.+)", execute: extractData}}, {"--data-urlencode", 10, parseBodyURLEncode, &extract{re: "--data-urlencode +(.+)", execute: extractData}}, {"--data-binary", 10, parseBodyBinary, &extract{re: "--data-binary +(.+)", execute: extractData}}, {"--data-ascii", 10, parseBodyASCII, &extract{re: "--data-ascii +(.+)", execute: extractData}}, {"--data-raw", 10, parseBodyRaw, &extract{re: "--data-raw +(.+)", execute: extractData}}, //"--" {"--header", 10, parseHeader, nil}, {"--insecure", 15, parseInsecure, nil}, {"--call", 10, parseCallBack, &extract{re: "--call +(.+)", execute: extractData}}, {"--user-agent", 15, parseUserAgent, &extract{re: "--user-agent +(.+)", execute: extractData}}, {"--user", 15, parseUser, &extract{re: "--user +(.+)", execute: extractData}}, {"--connect-timeout", 15, parseTimeout, &extract{re: "--connect-timeout +(.+)", execute: extractData}}, } for _, oe := range oelist { optionTrie.Insert(oe) } log.Println("support options:", optionTrie.AllWords()) } // ParseRawCURL curl_bash 可以用trie改进 没空改 func ParseRawCURL(scurl string) (cURL *CURL, err error) { defer func() { if _err := recover(); _err != nil { cURL = nil err = _err.(error) } }() executor := newPQueueExecute() curl := NewCURL() if scurl[0] == '"' && scurl[len(scurl)-1] == '"' { scurl = strings.Trim(scurl, `"`) } else if scurl[0] == '\'' && scurl[len(scurl)-1] == '\'' { scurl = strings.Trim(scurl, `'`) } scurl = strings.TrimSpace(scurl) scurl = strings.TrimLeft(scurl, "curl") mathches := regexp.MustCompile(`--[^ ]+ +'[^']+'|--[^ ]+ +[^ ]+|-[A-Za-z] +'[^']+'|-[A-Za-z] +[^ ]+| '[^']+'|--[a-z]+ {0,}`).FindAllString(scurl, -1) for _, m := range mathches { m = strings.TrimSpace(m) switch v := m[0]; v { case '\'': purl, err := url.Parse(strings.Trim(m, "'")) if err != nil { panic(err) } curl.ParsedURL = purl case '-': exec := judgeOptions(curl, m) if exec != nil { executor.Push(exec) } } } for executor.Len() > 0 { exec := executor.Pop() exec.Execute() } if curl.Method == "" { curl.Method = "GET" } return curl, nil } func judgeOptions(u *CURL, soption string) *parseFunction { word := TrieStrWord(soption) if ioe := optionTrie.SearchMostPrefix(&word); ioe != nil { oe := ioe.(*optionExecute) return oe.BuildFunction(u, soption) } log.Println(soption, " no haved this option") return nil } func judgeAndParseOptions(u *CURL, soption string) *parseFunction { switch prefix := soption[0:2]; prefix { case "-H": return &parseFunction{ParamCURL: u, ParamData: soption, ExecuteFunction: parseHeader, Priority: 10} case "-X": return &parseFunction{ParamCURL: u, ParamData: soption, ExecuteFunction: parseOptX, Priority: 10} case "-A": // User-Agent 先后顺序的问题 data := extractData("^-A +(.+)", soption) return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseUserAgent, Priority: 15} case "-I": return &parseFunction{ParamCURL: u, ParamData: soption, ExecuteFunction: parseOptI, Priority: 15} case "--": return parseLongOption(u, soption) case "-d": data := extractData("^-d +(.+)", soption) return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseBodyASCII, Priority: 10} case "-u": data := extractData("^-u +(.+)", soption) return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseUser, Priority: 15} case "-k": // -k, --insecure Allow insecure server connections when using SSL return &parseFunction{ParamCURL: u, ParamData: soption, ExecuteFunction: parseInsecure, Priority: 15} } return nil } func parseLongOption(u *CURL, soption string) *parseFunction { // -d, --data HTTP POST data // --data-ascii HTTP POST ASCII data // --data-binary HTTP POST binary data // --data-raw HTTP POST data, '@' allowed // --data-urlencode HTTP POST data url encoded switch { case regexp.MustCompile("^--data |^--data-urlencode|^--data-binary|^--data-ascii|^--data-raw").MatchString(soption): datas := regexp.MustCompile("^--data-(binary) +(.+)|^--data-(ascii) +(.+)|^--data-(raw) +(.+)|^--data-(urlencode) +(.+)|^--(data) +(.+)").FindStringSubmatch(soption) dtype := datas[1] data := strings.Trim(datas[2], "'") switch dtype { case "binary": return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseBodyBinary, Priority: 10} case "ascii": return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseBodyASCII, Priority: 10} case "raw": return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseBodyRaw, Priority: 10} case "urlencode": return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseBodyURLEncode, Priority: 10} case "data": return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseBodyASCII, Priority: 10} } case regexp.MustCompile("^--header").MatchString(soption): return &parseFunction{ParamCURL: u, ParamData: soption, ExecuteFunction: parseHeader, Priority: 10} case regexp.MustCompile("^--call").MatchString(soption): data := extractData("^--call +(.+)", soption) return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseCallBack, Priority: 10} case regexp.MustCompile("^--user-agent").MatchString(soption): data := extractData("^--user-agent +(.+)", soption) return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseUserAgent, Priority: 15} case regexp.MustCompile("^--user").MatchString(soption): data := extractData("^--user +(.+)", soption) return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseUser, Priority: 15} case regexp.MustCompile("^--insecure").MatchString(soption): return &parseFunction{ParamCURL: u, ParamData: soption, ExecuteFunction: parseInsecure, Priority: 15} case regexp.MustCompile("^--connect-timeout").MatchString(soption): data := extractData("^--connect-timeout +(.+)", soption) return &parseFunction{ParamCURL: u, ParamData: data, ExecuteFunction: parseTimeout, Priority: 15} } log.Println("can't parseOption", soption) return nil } func extractData(re, soption string) string { datas := regexp.MustCompile(re).FindStringSubmatch(soption) return strings.Trim(datas[1], "'") } func parseCallBack(u *CURL, value string) { u.CallBack = value } func parseTimeout(u *CURL, value string) { timeout, err := strconv.Atoi(value) if err != nil { panic(err) } u.Timeout = timeout } func parseInsecure(u *CURL, soption string) { u.Insecure = true } func parseUser(u *CURL, soption string) { auth := strings.Split(soption, ":") u.Auth = &requests.BasicAuth{User: auth[0], Password: auth[1]} } func parseUserAgent(u *CURL, value string) { u.Header.Add("User-Agent", value) } func parseOptI(u *CURL, soption string) { u.Method = "HEAD" } func parseOptX(u *CURL, soption string) { matches := regexp.MustCompile("-X +(.+)").FindStringSubmatch(soption) method := strings.Trim(matches[1], "'") u.Method = method } func parseBodyURLEncode(u *CURL, data string) { if u.Method != "" { u.Method = "POST" } u.Body.SetPrefix(requests.TypeURLENCODED) u.Body.SetIOBody(data) } func parseBodyRaw(u *CURL, data string) { if u.Method != "" { u.Method = "POST" } u.Body.SetPrefix(requests.TypeURLENCODED) u.Body.SetIOBody(data) } func parseBodyASCII(u *CURL, data string) { if u.Method != "" { u.Method = "POST" } u.Body.SetPrefix(requests.TypeURLENCODED) if data[0] != '@' { u.Body.SetIOBody(data) } else { f, err := os.Open(data[1:]) if err != nil { panic(err) } defer f.Close() bdata, err := ioutil.ReadAll(f) if err != nil { panic(err) } u.Body.SetIOBody(bdata) } } // 处理@ 并且替/r/n符号 func parseBodyBinary(u *CURL, data string) { if u.Method != "" { u.Method = "POST" } u.Body.SetPrefix(requests.TypeURLENCODED) if data[0] != '@' { u.Body.SetIOBody(data) } else { f, err := os.Open(data[1:]) if err != nil { panic(err) } defer f.Close() bdata, err := ioutil.ReadAll(f) if err != nil { panic(err) } bdata = regexp.MustCompile("\n|\r").ReplaceAll(bdata, []byte("")) u.Body.SetIOBody(bdata) } } func parseHeader(u *CURL, soption string) { matches := regexp.MustCompile(`'([^:]+): ([^']+)'`).FindAllStringSubmatch(soption, 1)[0] key := matches[1] value := matches[2] switch key { case "Cookie": u.Cookies = ReadRawCookies(value, "") u.CookieJar.SetCookies(u.ParsedURL, u.Cookies) case "Content-Type": u.Body.SetPrefix(value) default: u.Header.Add(key, value) } }