Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,10 @@ public class LinkHeaderPaginationStrategy<T>
): Page<T> {
val items: List<T> = itemsExtractor(response)
// Some servers emit multiple Link headers (one per link-value) instead of a single
// comma-separated header. Join with ',' so a single parser handles both shapes.
val linkValues: List<String> = response.headers.values(linkHeader)
val combined: String =
if (linkValues.isEmpty()) "" else linkValues.joinToString(separator = ",")
val nextUrlString: String? = if (combined.isEmpty()) null else extractNextUrl(combined)
// comma-separated header. Join with ',' so a single parser handles both shapes; an
// empty values list joins to "", and extractNextUrl("") already returns null.
val nextUrlString: String? =
extractNextUrl(response.headers.values(linkHeader).joinToString(separator = ","))
// Resolve the (possibly relative) next-link target against the originating page's
// URL. A target that cannot be parsed into a valid URL ends the stream instead of
// aborting the whole iteration with a MalformedURLException.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,10 @@ public class Paginator<T>
// Index into currentPage.items for the next item to emit.
private var currentItemIndex: Int = 0

// null = first iteration (use initialRequest); else the next-page request scheduled by
// the previous page's strategy.parse() call.
private var nextRequest: Request? = null

// true once we have fetched at least one page. Used to distinguish "never fetched" from
// "exhausted after fetching" — both have currentPage == null right after iteration ends.
private var started: Boolean = false
// The next request to fetch: seeded with initialRequest, then replaced after each page
// with that page's nextPageRequest(), or null once a page reports no successor (which
// ends the stream on the following advance()).
private var nextRequest: Request? = initialRequest

// true after iteration is definitively over; prevents further fetches.
private var done: Boolean = false
Expand Down Expand Up @@ -190,26 +187,11 @@ public class Paginator<T>
* [currentPage]; `false` if iteration is now done.
*/
private fun advance(): Boolean {
if (pagesFetched >= maxPages) {
// Safety cap reached: stop before fetching a page we would otherwise yield,
// even if the previous page still reports hasNext.
done = true
currentPage = null
return false
}
val request: Request? =
if (!started) {
initialRequest
} else {
// We have a current page that's exhausted — see if it can produce a next.
val finishedPage = currentPage
if (finishedPage == null || !finishedPage.hasNext) {
null
} else {
nextRequest ?: finishedPage.nextPageRequest()
}
}
if (request == null) {
val request = nextRequest
if (request == null || pagesFetched >= maxPages) {
// Either no next request is scheduled (the previous page had no successor) or
// the safety cap is reached — stop before fetching a page we would otherwise
// yield, even if the previous page still reports hasNext.
done = true
currentPage = null
return false
Expand All @@ -222,11 +204,10 @@ public class Paginator<T>
} finally {
response.close()
}
started = true
currentPage = page
currentItemIndex = 0
// Compute the next request now so we don't have to retain the (closed) response.
// If this page has no next, nextRequest stays null; advance() will treat that as done.
// If this page has no next, nextRequest goes null; the next advance() ends the stream.
nextRequest = if (page.hasNext) page.nextPageRequest() else null
return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,9 @@ internal object RequestRebuilder {
if (existing.isEmpty()) return null
for (segment in existing.split('&')) {
if (segment.isEmpty()) continue
val eq = segment.indexOf('=')
val rawKey = if (eq >= 0) segment.substring(0, eq) else segment
if (decodeOrRaw(rawKey) == name) {
val rawValue = if (eq >= 0) segment.substring(eq + 1) else ""
return decodeOrRaw(rawValue)
val key = segment.substringBefore('=', segment)
if (decodeOrRaw(key) == name) {
return decodeOrRaw(segment.substringAfter('=', ""))
}
}
return null
Expand Down
Loading