override fun install()

in ktor-server/ktor-server-core/jvm/src/io/ktor/features/ContentNegotiation.kt [103:181]


        override fun install(
            pipeline: ApplicationCallPipeline,
            configure: Configuration.() -> Unit
        ): ContentNegotiation {
            val configuration = Configuration().apply(configure)
            val feature = ContentNegotiation(configuration.registrations, configuration.acceptContributors)

            // Respond with "415 Unsupported Media Type" if content cannot be transformed on receive
            pipeline.intercept(ApplicationCallPipeline.Features) {
                try {
                    proceed()
                } catch (e: UnsupportedMediaTypeException) {
                    call.respond(HttpStatusCode.UnsupportedMediaType)
                }
            }

            pipeline.sendPipeline.intercept(ApplicationSendPipeline.Render) { subject ->
                if (subject is OutgoingContent) return@intercept

                val acceptHeaderContent = call.request.header(HttpHeaders.Accept)
                val acceptHeader = try {
                    parseHeaderValue(acceptHeaderContent)
                        .map { ContentTypeWithQuality(ContentType.parse(it.value), it.quality) }
                } catch (parseFailure: BadContentTypeFormatException) {
                    throw BadRequestException(
                        "Illegal Accept header format: $acceptHeaderContent",
                        parseFailure
                    )
                }

                val acceptItems = feature.acceptContributors.fold(acceptHeader) { acc, e ->
                    e(call, acc)
                }.distinct().sortedByQuality()

                val suitableConverters = if (acceptItems.isEmpty()) {
                    // all converters are suitable since client didn't indicate what it wants
                    feature.registrations
                } else {
                    // select converters that match specified Accept header, in order of quality
                    acceptItems.flatMap { (contentType, _) ->
                        feature.registrations.filter { it.contentType.match(contentType) }
                    }.distinct()
                }

                // Pick the first one that can convert the subject successfully
                val converted = suitableConverters.mapFirstNotNull {
                    it.converter.convertForSend(this, it.contentType, subject)
                }

                val rendered = converted?.let { transformDefaultContent(it) }
                    ?: HttpStatusCodeContent(HttpStatusCode.NotAcceptable)
                proceedWith(rendered)
            }

            pipeline.receivePipeline.intercept(ApplicationReceivePipeline.Transform) { receive ->
                // skip if already transformed
                if (subject.value !is ByteReadChannel) return@intercept
                // skip if a byte channel has been requested so there is nothing to negotiate
                if (subject.type == ByteReadChannel::class) return@intercept

                val requestContentType = try {
                    call.request.contentType().withoutParameters()
                } catch (parseFailure: BadContentTypeFormatException) {
                    throw BadRequestException(
                        "Illegal Content-Type header format: ${call.request.headers[HttpHeaders.ContentType]}",
                        parseFailure
                    )
                }
                val suitableConverter =
                    feature.registrations.firstOrNull { converter -> requestContentType.match(converter.contentType) }
                        ?: throw UnsupportedMediaTypeException(requestContentType)

                val converted = suitableConverter.converter.convertForReceive(this)
                    ?: throw UnsupportedMediaTypeException(requestContentType)

                proceedWith(ApplicationReceiveRequest(receive.typeInfo, converted, reusableValue = true))
            }
            return feature
        }