mirror of
https://github.com/aykhans/dodo.git
synced 2025-12-13 15:59:19 +00:00
184 lines
5.2 KiB
Go
184 lines
5.2 KiB
Go
package requests
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/aykhans/dodo/config"
|
|
customerrors "github.com/aykhans/dodo/custom_errors"
|
|
"github.com/aykhans/dodo/utils"
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// Run executes the HTTP requests based on the provided request configuration.
|
|
// It checks for internet connection and returns an error if there is no connection.
|
|
// If the context is canceled while checking proxies, it returns the ErrInterrupt.
|
|
// If the context is canceled while sending requests, it returns the response objects obtained so far.
|
|
func Run(ctx context.Context, requestConfig *config.RequestConfig) (Responses, error) {
|
|
checkConnectionCtx, checkConnectionCtxCancel := context.WithTimeout(ctx, 8*time.Second)
|
|
if !checkConnection(checkConnectionCtx) {
|
|
checkConnectionCtxCancel()
|
|
return nil, customerrors.ErrNoInternet
|
|
}
|
|
checkConnectionCtxCancel()
|
|
|
|
clientDoFunc := getClientDoFunc(
|
|
ctx,
|
|
requestConfig.Timeout,
|
|
requestConfig.Proxies,
|
|
requestConfig.GetValidDodosCountForProxies(),
|
|
requestConfig.GetMaxConns(fasthttp.DefaultMaxConnsPerHost),
|
|
requestConfig.Yes,
|
|
requestConfig.URL,
|
|
)
|
|
if clientDoFunc == nil {
|
|
return nil, customerrors.ErrInterrupt
|
|
}
|
|
|
|
requests, err := getRequests(
|
|
ctx,
|
|
requestConfig.URL,
|
|
requestConfig.Headers,
|
|
requestConfig.Cookies,
|
|
requestConfig.Params,
|
|
requestConfig.Method,
|
|
requestConfig.Body,
|
|
requestConfig.RequestCount,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
responses := releaseDodos(
|
|
ctx,
|
|
requests,
|
|
clientDoFunc,
|
|
requestConfig.GetValidDodosCountForRequests(),
|
|
)
|
|
if ctx.Err() != nil && len(responses) == 0 {
|
|
return nil, customerrors.ErrInterrupt
|
|
}
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
// releaseDodos sends HTTP requests concurrently using multiple "dodos" (goroutines).
|
|
//
|
|
// Parameters:
|
|
// - ctx: The context to control the lifecycle of the requests.
|
|
// - requests: A slice of HTTP requests to be sent.
|
|
// - clientDoFunc: A function to execute the HTTP requests.
|
|
// - dodosCount: The number of dodos (goroutines) to use for sending the requests.
|
|
//
|
|
// Returns:
|
|
// - A slice of Response objects containing the results of the requests.
|
|
//
|
|
// The function divides the requests into equal parts based on the number of dodos.
|
|
// It then sends each part concurrently using a separate goroutine.
|
|
func releaseDodos(
|
|
ctx context.Context,
|
|
requests []*fasthttp.Request,
|
|
clientDoFunc ClientDoFunc,
|
|
dodosCount uint,
|
|
) Responses {
|
|
var (
|
|
wg sync.WaitGroup
|
|
streamWG sync.WaitGroup
|
|
requestCountPerDodo uint
|
|
dodosCountInt int = int(dodosCount)
|
|
totalRequestCount uint = uint(len(requests))
|
|
requestCount uint = 0
|
|
responses = make([][]*Response, dodosCount)
|
|
increase = make(chan int64, totalRequestCount)
|
|
)
|
|
|
|
wg.Add(dodosCountInt)
|
|
streamWG.Add(1)
|
|
streamCtx, streamCtxCancel := context.WithCancel(context.Background())
|
|
|
|
go streamProgress(streamCtx, &streamWG, int64(totalRequestCount), "Dodos Working🔥", increase)
|
|
|
|
for i := range dodosCount {
|
|
if i+1 == dodosCount {
|
|
requestCountPerDodo = totalRequestCount - (i * totalRequestCount / dodosCount)
|
|
} else {
|
|
requestCountPerDodo = ((i + 1) * totalRequestCount / dodosCount) -
|
|
(i * totalRequestCount / dodosCount)
|
|
}
|
|
|
|
go sendRequest(
|
|
ctx,
|
|
requests[requestCount:requestCount+requestCountPerDodo],
|
|
&responses[i],
|
|
increase,
|
|
clientDoFunc,
|
|
&wg,
|
|
)
|
|
requestCount += requestCountPerDodo
|
|
}
|
|
wg.Wait()
|
|
streamCtxCancel()
|
|
streamWG.Wait()
|
|
return utils.Flatten(responses)
|
|
}
|
|
|
|
// sendRequest sends multiple HTTP requests concurrently and collects their responses.
|
|
//
|
|
// Parameters:
|
|
// - ctx: The context to control cancellation and timeout.
|
|
// - requests: A slice of pointers to fasthttp.Request objects to be sent.
|
|
// - responseData: A pointer to a slice of *Response objects to store the results.
|
|
// - increase: A channel to signal the completion of each request.
|
|
// - clientDo: A function to execute the HTTP request.
|
|
// - wg: A wait group to synchronize the completion of the requests.
|
|
//
|
|
// The function iterates over the provided requests, sending each one using the clientDo function.
|
|
// It measures the time taken for each request and appends the response data to responseData.
|
|
// If an error occurs, it appends an error response. The function signals completion through the increase channel
|
|
// and ensures proper resource cleanup by releasing requests and responses.
|
|
func sendRequest(
|
|
ctx context.Context,
|
|
requests []*fasthttp.Request,
|
|
responseData *[]*Response,
|
|
increase chan<- int64,
|
|
clientDo ClientDoFunc,
|
|
wg *sync.WaitGroup,
|
|
) {
|
|
defer wg.Done()
|
|
|
|
for _, request := range requests {
|
|
if ctx.Err() != nil {
|
|
return
|
|
}
|
|
|
|
func() {
|
|
defer fasthttp.ReleaseRequest(request)
|
|
startTime := time.Now()
|
|
response, err := clientDo(ctx, request)
|
|
completedTime := time.Since(startTime)
|
|
|
|
if err != nil {
|
|
if err == customerrors.ErrInterrupt {
|
|
return
|
|
}
|
|
*responseData = append(*responseData, &Response{
|
|
StatusCode: 0,
|
|
Error: err,
|
|
Time: completedTime,
|
|
})
|
|
increase <- 1
|
|
return
|
|
}
|
|
defer fasthttp.ReleaseResponse(response)
|
|
|
|
*responseData = append(*responseData, &Response{
|
|
StatusCode: response.StatusCode(),
|
|
Error: nil,
|
|
Time: completedTime,
|
|
})
|
|
increase <- 1
|
|
}()
|
|
}
|
|
}
|