// Spawn sends the actual request. func (r *Request) Spawn(parent *Response, wg *sync.WaitGroup) { body := "" if r.Data != nil { body = r.Data.Format(parent) } greq := goreq.Request{Method: r.Method, Body: body, UserAgent: profile.UserAgent} // Let's set the headers, if needed. if r.Headers != nil { for _, line := range strings.Split(r.Headers.Format(parent), "\n") { line = strings.TrimSpace(line) if line == "" { continue } hdr := strings.Split(line, ":") greq.AddHeader(strings.TrimSpace(hdr[0]), strings.TrimSpace(hdr[1])) } } // Let's also add the cookies. if r.FwdCookies && parent != nil { if parent.cookies != nil { for _, delicacy := range parent.cookies { greq.AddCookie(delicacy) } } } // One go routine which pops responses from the channel and moves them to the list. go func() { for { r.doneReqs = append(r.doneReqs, <-r.doneChan) r.doneWg.Done() perc := float64(len(r.doneReqs)) / float64(r.Repeat) notify := false if perc >= 0.75 && perc-0.75 < 1e-4 { notify = true } else if perc >= 0.5 && perc-0.5 < 1e-4 { notify = true } else if perc >= 0.25 && perc-0.25 < 1e-4 { notify = true } else if len(r.doneReqs)%100 == 0 { notify = true } if notify { log.Notice("Completed %d requests out of %d to %s.", len(r.doneReqs), r.Repeat, r.URL) } } }() // Let's spawn all the requests, with their respective concurrency. wg.Add(r.Repeat) r.doneWg.Add(r.Repeat) for rno := 1; rno <= r.Repeat; rno++ { go func(no int, greq goreq.Request) { r.ongoingReqs <- struct{}{} // Adding sentinel value to limit concurrency. greq.Uri = r.URL.Generate() resp := Response{} startTime := time.Now() gresp, err := greq.Do() resp.FromGoResp(gresp, err, startTime) if err != nil { log.Critical("could not send request to #%d %s: %s", no, r.URL, err) } <-r.ongoingReqs // We're done, let's make room for the next request. resp.duration = time.Since(startTime) // Let's add that request to the list of completed requests. r.doneChan <- &resp runtime.Gosched() }(rno, greq) } // Let's now have a go routine which waits for all the requests to complete // and spawns all the children. go func() { r.doneWg.Wait() if r.Children != nil { log.Debug("Spawning children for %s.", r.URL) for _, child := range r.Children { // Note that we always use the LAST response as the parent response. child.Spawn(r.doneReqs[0], wg) } } log.Debug("Computing result of %s.", r.URL) r.ComputeResult(wg) }() }