@@ -2729,6 +2729,84 @@ static void do_throttle_wait(struct gh__request_params *params,
2729
2729
}
2730
2730
}
2731
2731
2732
+ static void set_main_creds_on_slot (struct active_request_slot * slot ,
2733
+ const struct credential * creds )
2734
+ {
2735
+ assert (creds == & gh__global .main_creds );
2736
+
2737
+ /*
2738
+ * When talking to the main/origin server, we have 3 modes
2739
+ * of operation:
2740
+ *
2741
+ * [1] The initial request is sent without loading creds
2742
+ * and with ANY-AUTH set. (And the `":"` is a magic
2743
+ * value.)
2744
+ *
2745
+ * This allows libcurl to negotiate for us if it can.
2746
+ * For example, this allows NTLM to work by magic and
2747
+ * we get 200s without ever seeing a 401. If libcurl
2748
+ * cannot negotiate for us, it gives us a 401 (and all
2749
+ * of the 401 code in this file responds to that).
2750
+ *
2751
+ * [2] A 401 retry will load the main creds and try again.
2752
+ * This causes `creds->username`to be non-NULL (even
2753
+ * if refers to a zero-length string). And we assume
2754
+ * BASIC Authentication. (And a zero-length username
2755
+ * is a convention for PATs, but then sometimes users
2756
+ * put the PAT in their `username` field and leave the
2757
+ * `password` field blank. And that works too.)
2758
+ *
2759
+ * [3] Subsequent requests on the same connection use
2760
+ * whatever worked before.
2761
+ */
2762
+ if (creds && creds -> username ) {
2763
+ curl_easy_setopt (slot -> curl , CURLOPT_HTTPAUTH , CURLAUTH_BASIC );
2764
+ curl_easy_setopt (slot -> curl , CURLOPT_USERNAME , creds -> username );
2765
+ curl_easy_setopt (slot -> curl , CURLOPT_PASSWORD , creds -> password );
2766
+ } else {
2767
+ curl_easy_setopt (slot -> curl , CURLOPT_HTTPAUTH , CURLAUTH_ANY );
2768
+ curl_easy_setopt (slot -> curl , CURLOPT_USERPWD , ":" );
2769
+ }
2770
+ }
2771
+
2772
+ static void set_cache_server_creds_on_slot (struct active_request_slot * slot ,
2773
+ const struct credential * creds )
2774
+ {
2775
+ assert (creds == & gh__global .cache_creds );
2776
+ assert (creds -> username );
2777
+
2778
+ /*
2779
+ * Things are weird when talking to a cache-server:
2780
+ *
2781
+ * [1] They don't send 401s on an auth error, rather they send
2782
+ * a 400 (with a nice human-readable string in the html body).
2783
+ * This prevents libcurl from doing any negotiation for us.
2784
+ *
2785
+ * [2] Cache-servers don't manage their own passwords, but
2786
+ * rather require us to send the Basic Authentication
2787
+ * username & password that we would send to the main
2788
+ * server. (So yes, we have to get creds validated
2789
+ * against the main server creds and substitute them when
2790
+ * talking to the cache-server.)
2791
+ *
2792
+ * This means that:
2793
+ *
2794
+ * [a] We cannot support cache-servers that want to use NTLM.
2795
+ *
2796
+ * [b] If we want to talk to a cache-server, we have get the
2797
+ * Basic Auth creds for the main server. And this may be
2798
+ * problematic if the libcurl and/or the credential manager
2799
+ * insists on using NTML and prevents us from getting them.
2800
+ *
2801
+ * So we never try AUTH-ANY and force Basic Auth (if possible).
2802
+ */
2803
+ if (creds && creds -> username ) {
2804
+ curl_easy_setopt (slot -> curl , CURLOPT_HTTPAUTH , CURLAUTH_BASIC );
2805
+ curl_easy_setopt (slot -> curl , CURLOPT_USERNAME , creds -> username );
2806
+ curl_easy_setopt (slot -> curl , CURLOPT_PASSWORD , creds -> password );
2807
+ }
2808
+ }
2809
+
2732
2810
/*
2733
2811
* Do a single HTTP request WITHOUT robust-retry, auth-retry or fallback.
2734
2812
*/
@@ -2794,37 +2872,10 @@ static void do_req(const char *url_base,
2794
2872
curl_easy_setopt (slot -> curl , CURLOPT_HEADERFUNCTION , parse_resp_hdr );
2795
2873
curl_easy_setopt (slot -> curl , CURLOPT_HEADERDATA , params );
2796
2874
2797
- if (creds && creds -> username ) {
2798
- /*
2799
- * Force CURL to respect the username/password we provide by
2800
- * turning off the AUTH-ANY negotiation stuff.
2801
- *
2802
- * That is, CURLAUTH_ANY causes CURL to NOT send the creds
2803
- * on an initial request in order to force a 401 and let it
2804
- * negotiate the best auth scheme and then retry.
2805
- *
2806
- * This is problematic when talking to the cache-servers
2807
- * because they send a 400 (with a "A valid Basic Auth..."
2808
- * message body) rather than a 401. This means that the
2809
- * the automatic retry will never happen. And even if we
2810
- * do force a retry, CURL still won't send the creds.
2811
- *
2812
- * So we turn it off and force it use our creds.
2813
- */
2814
- curl_easy_setopt (slot -> curl , CURLOPT_HTTPAUTH , CURLAUTH_BASIC );
2815
- curl_easy_setopt (slot -> curl , CURLOPT_USERNAME , creds -> username );
2816
- curl_easy_setopt (slot -> curl , CURLOPT_PASSWORD , creds -> password );
2817
- } else {
2818
- /*
2819
- * Turn on the AUTH-ANY negotiation. This only works
2820
- * with the main Git server (because the cache-server
2821
- * doesn't handle 401s).
2822
- *
2823
- * TODO Think about if we really need to handle this case.
2824
- * TODO Guard with "if (params->sever_type == __MAIN)"
2825
- */
2826
- curl_easy_setopt (slot -> curl , CURLOPT_HTTPAUTH , CURLAUTH_ANY );
2827
- }
2875
+ if (params -> server_type == GH__SERVER_TYPE__MAIN )
2876
+ set_main_creds_on_slot (slot , creds );
2877
+ else
2878
+ set_cache_server_creds_on_slot (slot , creds );
2828
2879
2829
2880
if (params -> progress_base_phase2_msg .len ||
2830
2881
params -> progress_base_phase3_msg .len ) {
@@ -2923,27 +2974,8 @@ static void do_req__to_main(const char *url_component,
2923
2974
params -> server_type = GH__SERVER_TYPE__MAIN ;
2924
2975
2925
2976
/*
2926
- * When talking to the main Git server, we do allow non-auth'd
2927
- * initial requests (mainly for testing, since production servers
2928
- * will usually always want creds), so we DO NOT force load the
2929
- * user's creds here and let the normal 401 mechanism handle things.
2930
- * This has a slight perf penalty -- if we're bulk fetching 4000
2931
- * objects from a production server, we're going to do a 100K+ byte
2932
- * POST just to get a 401 and then repeat the 100K+ byte POST
2933
- * with the creds.
2934
- *
2935
- * TODO Consider making a test or runtime flag to alter this.
2936
- * TODO If set/not-set, add a call here to pre-populate the creds.
2937
- * TODO
2938
- * TODO lookup_main_creds();
2939
- * TODO
2940
- *
2941
- * TODO Testing with GIT_TRACE_CURL shows that curl will *SOMETIME*
2942
- * TODO send an "Expect: 100-continue" and silently handle/eat the
2943
- * TODO "HTTP/1.1 100 Continue" before the POST-body is sent,
2944
- * TODO so maybe this isn't an issue. (Other than the extra
2945
- * TODO roundtrip for the first set of headers.) More testing here
2946
- * TODO should be performed.
2977
+ * When talking to the main Git server, we DO NOT preload the
2978
+ * creds before the first request.
2947
2979
*/
2948
2980
2949
2981
do_req__with_robust_retry (gh__global .main_url , url_component ,
@@ -2968,6 +3000,10 @@ static void do_req__to_cache_server(const char *url_component,
2968
3000
{
2969
3001
params -> server_type = GH__SERVER_TYPE__CACHE ;
2970
3002
3003
+ /*
3004
+ * When talking to a cache-server, DO force load the creds.
3005
+ * This implicitly preloads the creds to the main server.
3006
+ */
2971
3007
synthesize_cache_server_creds ();
2972
3008
2973
3009
do_req__with_robust_retry (gh__global .cache_server_url , url_component ,
0 commit comments