@@ -109,6 +109,113 @@ template <size_t N> void safe_strcpy(char (&dest)[N], const std::string &src)
109109 dest[len] = ' \0 ' ;
110110}
111111
112+ #if defined(HAVE_CURL) && defined(GRK_HAS_LIBCURL)
113+
114+ /* *
115+ * @brief True if @p filename is a network path handled natively by Grok.
116+ *
117+ * Both /vsis3/ and /vsicurl/ are fetched via libcurl inside Grok; /vsis3/
118+ * needs AWS credentials resolved by GDAL, /vsicurl/ only needs the shared
119+ * HTTP auth options (.netrc, cookies, allow-insecure).
120+ */
121+ static bool isGrokNetworkPath (const char *filename)
122+ {
123+ return strncmp (filename, " /vsis3/" , 7 ) == 0 ||
124+ strncmp (filename, " /vsicurl/" , 9 ) == 0 ;
125+ }
126+
127+ /* *
128+ * @brief Forward GDAL's shared HTTP auth config options to grk_stream_params.
129+ *
130+ * These options apply to any network transport (both /vsis3/ and /vsicurl/),
131+ * so they are set exactly once on the shared path
132+ * - GDAL_HTTP_UNSAFESSL -> s3_allow_insecure
133+ * - GDAL_HTTP_NETRC -> netrc (default YES, matching GDAL)
134+ * - GDAL_HTTP_NETRC_FILE -> netrc_file
135+ * - GDAL_HTTP_COOKIE -> cookie
136+ * - GDAL_HTTP_COOKIEFILE -> cookie_file
137+ * - GDAL_HTTP_COOKIEJAR -> cookie_jar
138+ * - GDAL_HTTP_USERPWD -> username / password
139+ * - GDAL_HTTP_BEARER -> bearer_token
140+ * - GDAL_HTTP_PROXY -> proxy
141+ * - GDAL_HTTP_PROXYUSERPWD -> proxy_userpwd
142+ * - GDAL_HTTP_USERAGENT -> user_agent
143+ * - GDAL_HTTP_TIMEOUT -> timeout
144+ * - GDAL_HTTP_CONNECTTIMEOUT -> connect_timeout
145+ * - GDAL_HTTP_MAX_RETRY -> max_retry
146+ * - GDAL_HTTP_RETRY_DELAY -> retry_delay
147+ */
148+ static void forwardSharedHttpAuth (grk_stream_params &streamParams)
149+ {
150+ streamParams.s3_allow_insecure =
151+ CPLTestBool (CPLGetConfigOption (" GDAL_HTTP_UNSAFESSL" , " NO" ));
152+
153+ streamParams.netrc =
154+ CPLTestBool (CPLGetConfigOption (" GDAL_HTTP_NETRC" , " YES" ));
155+ if (const char *pszNetrcFile =
156+ CPLGetConfigOption (" GDAL_HTTP_NETRC_FILE" , nullptr ))
157+ safe_strcpy (streamParams.netrc_file , pszNetrcFile);
158+
159+ if (const char *pszCookie = CPLGetConfigOption (" GDAL_HTTP_COOKIE" , nullptr ))
160+ safe_strcpy (streamParams.cookie , pszCookie);
161+ if (const char *pszCookieFile =
162+ CPLGetConfigOption (" GDAL_HTTP_COOKIEFILE" , nullptr ))
163+ safe_strcpy (streamParams.cookie_file , pszCookieFile);
164+ if (const char *pszCookieJar =
165+ CPLGetConfigOption (" GDAL_HTTP_COOKIEJAR" , nullptr ))
166+ safe_strcpy (streamParams.cookie_jar , pszCookieJar);
167+
168+ // HTTP basic auth (user:password)
169+ if (const char *pszUserPwd =
170+ CPLGetConfigOption (" GDAL_HTTP_USERPWD" , nullptr ))
171+ {
172+ const std::string osUserPwd (pszUserPwd);
173+ const size_t nColon = osUserPwd.find (' :' );
174+ if (nColon != std::string::npos)
175+ {
176+ safe_strcpy (streamParams.username , osUserPwd.substr (0 , nColon));
177+ safe_strcpy (streamParams.password , osUserPwd.substr (nColon + 1 ));
178+ }
179+ }
180+
181+ // Bearer token for HTTP(S) endpoints
182+ if (const char *pszBearer = CPLGetConfigOption (" GDAL_HTTP_BEARER" , nullptr ))
183+ safe_strcpy (streamParams.bearer_token , pszBearer);
184+
185+ // Proxy
186+ if (const char *pszProxy = CPLGetConfigOption (" GDAL_HTTP_PROXY" , nullptr ))
187+ safe_strcpy (streamParams.proxy , pszProxy);
188+ if (const char *pszProxyUserPwd =
189+ CPLGetConfigOption (" GDAL_HTTP_PROXYUSERPWD" , nullptr ))
190+ safe_strcpy (streamParams.proxy_userpwd , pszProxyUserPwd);
191+
192+ // User agent
193+ if (const char *pszUserAgent =
194+ CPLGetConfigOption (" GDAL_HTTP_USERAGENT" , nullptr ))
195+ safe_strcpy (streamParams.user_agent , pszUserAgent);
196+
197+ // Timeouts
198+ const char *pszTimeout = CPLGetConfigOption (" GDAL_HTTP_TIMEOUT" , nullptr );
199+ if (pszTimeout)
200+ streamParams.timeout = atol (pszTimeout);
201+ const char *pszConnectTimeout =
202+ CPLGetConfigOption (" GDAL_HTTP_CONNECTTIMEOUT" , nullptr );
203+ if (pszConnectTimeout)
204+ streamParams.connect_timeout = atol (pszConnectTimeout);
205+
206+ // Retry configuration
207+ const char *pszMaxRetry =
208+ CPLGetConfigOption (" GDAL_HTTP_MAX_RETRY" , nullptr );
209+ if (pszMaxRetry)
210+ streamParams.max_retry = static_cast <uint32_t >(atol (pszMaxRetry));
211+ const char *pszRetryDelay =
212+ CPLGetConfigOption (" GDAL_HTTP_RETRY_DELAY" , nullptr );
213+ if (pszRetryDelay)
214+ streamParams.retry_delay = static_cast <uint32_t >(atol (pszRetryDelay));
215+ }
216+
217+ #endif // HAVE_CURL && GRK_HAS_LIBCURL
218+
112219/* ***********************************************************************/
113220/* GrokCanRead() */
114221/* ***********************************************************************/
@@ -128,10 +235,12 @@ static bool GrokCanRead(const char *filename)
128235 return false ;
129236
130237#if defined(HAVE_CURL) && defined(GRK_HAS_LIBCURL)
131- // Cloud storage: Grok handles S3 fetching natively via libcurl.
132- // GDAL resolves AWS credentials and passes them to Grok via
133- // grk_stream_params (username/password/bearer_token/region).
134- if (strncmp (filename, " /vsis3/" , 7 ) == 0 )
238+ // Cloud storage: Grok handles fetching natively via libcurl.
239+ // For /vsis3/, GDAL resolves AWS credentials and passes them to Grok
240+ // via grk_stream_params. For /vsicurl/, Grok's HTTPFetcher strips the
241+ // prefix and issues HTTP(S) range requests directly, honoring the
242+ // shared .netrc/cookie options forwarded from GDAL.
243+ if (isGrokNetworkPath (filename))
135244 return true ;
136245#endif
137246
@@ -225,7 +334,7 @@ static void JP2_DebugCallback(const char *pszMsg, CPL_UNUSED void *unused)
225334/* *
226335 * @brief VSILFILE write callback for Grok stream I/O.
227336 *
228- * Used when GrokCanRead() returns false (e.g. /vsimem/, /vsicurl /).
337+ * Used when GrokCanRead() returns false (e.g. /vsimem/, /vsitar /).
229338 * Also always used for compression output.
230339 */
231340static size_t JP2Dataset_Write (const uint8_t *pBuffer, size_t nBytes,
@@ -538,9 +647,21 @@ struct GRKCodecWrapper
538647 safe_strcpy (streamParams.file , pszFilename);
539648 streamParams.initial_offset = psJP2File->nBaseOffset ;
540649#if defined(HAVE_CURL) && defined(GRK_HAS_LIBCURL)
650+ // Shared HTTP auth options (.netrc, cookies, allow-insecure)
651+ // apply to both /vsis3/ and /vsicurl/ and are set exactly once
652+ // here so the S3-specific branch below does not duplicate them.
653+ if (isGrokNetworkPath (pszFilename))
654+ forwardSharedHttpAuth (streamParams);
655+
541656 // For /vsis3/ paths, resolve AWS credentials through GDAL's
542657 // full authentication chain and pass them to Grok so it can
543- // handle S3 fetching natively via libcurl.
658+ // handle S3 fetching natively via libcurl. For /vsicurl/,
659+ // Grok's HTTPFetcher strips the prefix and honors the shared
660+ // HTTP auth options forwarded above.
661+ //
662+ // S3 credentials override the generic USERPWD / BEARER values
663+ // set by forwardSharedHttpAuth, since the S3 signer requires
664+ // the access-key / secret / session-token triple.
544665 if (strncmp (pszFilename, " /vsis3/" , 7 ) == 0 )
545666 {
546667 auto poHelper = std::unique_ptr<VSIS3HandleHelper>(
@@ -572,8 +693,13 @@ struct GRKCodecWrapper
572693 streamParams.s3_no_sign_request =
573694 poHelper->GetCredentialsSource () ==
574695 AWSCredentialsSource::NO_SIGN_REQUEST ;
575- streamParams.s3_allow_insecure = CPLTestBool (
576- CPLGetConfigOption (" GDAL_HTTP_UNSAFESSL" , " NO" ));
696+
697+ // Requester pays
698+ const char *pszRequestPayer = VSIGetPathSpecificOption (
699+ pszFilename, " AWS_REQUEST_PAYER" , " " );
700+ if (pszRequestPayer[0 ])
701+ safe_strcpy (streamParams.request_payer ,
702+ pszRequestPayer);
577703 }
578704 else
579705 {
@@ -1330,6 +1456,7 @@ struct GRKCodecWrapper
13301456 *
13311457 * For local files and /vsis3/, uses native file I/O (closing the
13321458 * VSILFILE). For other VSI paths, uses VSILFILE callbacks.
1459+ * (/vsicurl/ is read-only so it never reaches the compression path.)
13331460 */
13341461 bool initCodec (const char *pszFilename, VSIVirtualHandleUniquePtr &fpOwner)
13351462 {
0 commit comments