net/httpサーバーでGo SDKを使う

著者:solvethree読了時間:1分

共有のsolvethreeクライアントを1つ作成し、認証付きPOSTからタスクIDを返して、別のGETでステータスを取得します。作成ハンドラー内でSolveを呼び出さないでください。この連携では、呼び出し元は元のリクエストを開いたままにせず、結果を待つことができます。

このページの内容

サーバーと対象を設定する

  1. 1.

    Go SDKをインストールする

    Go 1.22以降を使用してください。初期化済みのGoモジュールで、go get solvethree.com/[email protected]を実行して公式SDKをインストールします。以下の例をmain.goとして保存してください。

  2. 2.

    サーバー環境の設定

    SOLVETHREE_API_KEYにワークスペースのAPIキーを、INTEGRATION_KEYにこの内部サーバーの呼び出し元向けの別のシークレットを設定します。CAPTCHA_SITEには対象のホスト名を、SOLVETHREE_PROXYには解決に使用するHTTPまたはHTTPSプロキシのURLを設定してください。両方のキーを信頼できるサーバー上で保管してください。

  3. 3.

    設定した対象を準備する

    そのサイトの有効なプロファイルをワークスペースで利用できることを確認してください。サイトプロファイルの設定を参照してください。この例では、アクションをloginに固定しています。サーバーを実行する前に、対象が想定するアクションに変更してください。ホスト名とアクションを指定するだけでは、プロファイルは作成されません。

Go net/http · タスクを作成して取得
package main

import (
    "context"
    "encoding/json"
    "errors"
    "log"
    "net/http"
    "os"
    "time"
    solvethree "solvethree.com/sdk"
)

func main() {
    client, err := solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"), nil)
    if err != nil { log.Fatal(err) }
    secret := os.Getenv("INTEGRATION_KEY")
    if secret == "" { log.Fatal("Set INTEGRATION_KEY") }
    authorize := func(w http.ResponseWriter, r *http.Request) bool {
        if r.Header.Get("X-Integration-Key") != secret { http.Error(w, "Unauthorized", 401); return false }
        return true
    }
    respond := func(w http.ResponseWriter, result *solvethree.TaskResult, err error) {
        w.Header().Set("Content-Type", "application/json")
        if err != nil {
            var recovery *solvethree.Error
            w.WriteHeader(http.StatusBadGateway)
            ids := map[string]string{"error": "Solver request failed"}
            if errors.As(err, &recovery) { ids["requestId"] = recovery.RequestID; ids["taskId"] = recovery.TaskID }
            _ = json.NewEncoder(w).Encode(ids)
            return
        }
        _ = json.NewEncoder(w).Encode(result)
    }
    mux := http.NewServeMux()
    mux.HandleFunc("POST /captcha/tasks", func(w http.ResponseWriter, r *http.Request) {
        if !authorize(w,r) { return }
        input := solvethree.TaskRequest{Site: os.Getenv("CAPTCHA_SITE"), Action:"login",
            Proxy:os.Getenv("SOLVETHREE_PROXY"), RequestID:r.Header.Get("X-Request-ID")}
        if err := input.Validate(); err != nil { http.Error(w,"Invalid task input",400);return }
        ctx,cancel := context.WithTimeout(r.Context(),20*time.Second);defer cancel()
        zero:=0
        result,err:=client.CreateTask(ctx,input,&zero)
        respond(w,result,err)
    })
    mux.HandleFunc("GET /captcha/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
        if !authorize(w,r) { return }
        ctx,cancel:=context.WithTimeout(r.Context(),25*time.Second);defer cancel()
        result,err:=client.GetTask(ctx,r.PathValue("id"),15000)
        respond(w,result,err)
    })
    server:=&http.Server{Addr:"127.0.0.1:3000", Handler:mux, ReadHeaderTimeout:5*time.Second}
    log.Fatal(server.ListenAndServe())
}

go run .でモジュールを実行します。サーバーは127.0.0.1:3000で待ち受けます。Go標準のnet/httpルーターを使用するため、フレームワークは不要です。SDKクライアントは一度だけ作成され、並行して安全に使用できます。

タスクはデフォルトのenterprise-v3 reCAPTCHAモードを使用します。typeやurlではなく、サイトのホスト名、アクション、単一のプロキシを指定します。POST本文は使用しないため、呼び出し元が任意の対象やプロキシを選ぶことはできません。

一度POSTしてタスクIDを保存する

X-Integration-Keyヘッダーを付けてPOST /captcha/tasksを送信します。IDを受信する前に接続が切れてもタスクを取得できるよう、呼び出し元で生成したX-Request-IDも送信してください。1–64文字のASCII英字、数字、アンダースコア、ハイフンを使用します。

ハンドラーは設定されたタスクを検証し、待機値をゼロにしてCreateTaskを呼び出します。解決の完了を待たずに、タスクの作成を要求します。20秒のコンテキストは、このHTTPリクエストの待機時間を制限するものであり、解決のタイムアウトではありません。

成功すると、ハンドラーはSDKのタスク結果を返します。タスクIDを保存し、以降の確認にはGETルートを使用してください。POSTの成功を、使用可能なトークンが得られたことだと解釈しないでください。

どちらのルートにも連携キーが必要です。これは信頼できる内部インターフェースであり、ユーザーごとの認可ではありません。このキーを持つ呼び出し元は、設定されたワークスペースを通じてアクセスを共有します。

既存のタスクが完了するまでGETで取得する

同じ連携キーヘッダーを付けてGET /captcha/tasks/{id}を送信します。ハンドラーはGetTaskのみを呼び出し、代わりのタスクを作成することはありません。25秒のリクエストコンテキスト内で、最大15秒の待機を要求します。

  • pendingまたはsolving: 同じタスクIDを保持し、再度取得してください。
  • ready: 解決結果を読み取ってください。使用可能な結果があるのは、このステータスだけです。
  • failed: ポーリングを停止し、errorCodeとerrorを確認してください。別のタスクを作成するか判断する前に、原因を修正してください。

成功時のタスク結果としてデコードする前に、HTTPレスポンスのステータスを確認してください。このサーバーでは、認証されていないリクエストは401、無効なタスク入力は400、SDKリクエストエラーは502を返します。返されたタスクのステータスがfailedであることと、HTTPリクエストの失敗は別の結果です。

別の解決を作成せずにタイムアウトから復旧する

タイムアウトしても、作成に失敗したとは限りません。リクエストコンテキストをキャンセルすると、ローカルでの待機は停止しますが、リモートのタスクは停止しません。SDKが作成用POSTを自動的に再試行することはありません。

  1. 1.

    タスクIDがある場合は、そのタスクを取得する

    そのIDでGETルートを使用してください。SDKエラー時、この例のJSONエラーレスポンスには、SDKが公開している場合にtaskIdとrequestIdが含まれます。

  2. 2.

    リクエストIDしかない場合は、元の作成リクエストを再送する

    同じX-Request-IDを使い、タスクの入力を変更せずにPOSTを再送信してください。つまり、設定されたホスト名、アクション、プロキシを変更しないでください。同じリクエストIDとタスクリクエストで、既存のタスクを取得できます。そのIDで異なる入力を使うと、競合が発生します。

  3. 3.

    最初のリクエストの前にIDを保存する

    リクエストIDが指定されていない場合、SDKが生成しますが、接続が切れた呼び出し元は受信できないことがあります。POSTの前にX-Request-IDを決めて保持しておけば、復旧をエラーレスポンスに依存せずに行えます。

同じプロキシでreadyの結果を使用する

このサーバーは結果を返しますが、SDKが対象に送信するわけではありません。送信コードでは、トークン、返されたuserAgentとsecChUaのブラウザー識別情報、成功したプロキシを使用する必要があります。この単一プロキシの例では、解決後にIPを切り替えず、設定されたプロキシを引き続き使用してください。

readyのタスクは、解決結果が生成されたことを意味し、対象側がアプリケーションのリクエストを受け入れたことを意味するものではありません。送信に失敗した場合は、別の解決に料金を払う前に、送信するトークン、識別情報、プロキシ、対象へのリクエストを確認してください。

最初のタスクを実行しましょう。

$5のワークスペースクレジットで始められます。

ワークスペースを作成
net/httpサーバーでGo SDKを使う · solvethree