diff --git a/backend/mockingbird/src/main/resources/application.conf b/backend/mockingbird/src/main/resources/application.conf index 9b54efc3..f75ca1d7 100644 --- a/backend/mockingbird/src/main/resources/application.conf +++ b/backend/mockingbird/src/main/resources/application.conf @@ -27,10 +27,11 @@ ru.tinkoff.tcb { excludedRequestHeaders = [] excludedResponseHeaders = [] insecureHosts = [] + logOutgoingRequests = false } event { fetchInterval = "5 s" reloadInterval = "1 m" } -} \ No newline at end of file +} diff --git a/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/api/PublicApiHandler.scala b/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/api/PublicApiHandler.scala index edb904d9..3a26ad6c 100644 --- a/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/api/PublicApiHandler.scala +++ b/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/api/PublicApiHandler.scala @@ -151,40 +151,42 @@ final class PublicApiHandler( body: RequestBody )(uri: String, delay: Option[FiniteDuration], timeout: Option[FiniteDuration]): RIO[WLD, HttpStubResponse] = { val requestUri = uri"$uri".pipe(query.foldLeft(_) { case (u, (key, value)) => u.addParam(key, value) }) - log.debug(s"Received headers: ${headers.keys.mkString(", ")}") *> basicRequest - .headers(headers -- proxyConfig.excludedRequestHeaders) - .method(Method(method.entryName), requestUri) - .pipe(rt => - body match { - case AbsentRequestBody => rt - case SimpleRequestBody(value) => rt.body(value) - case MultipartRequestBody(value) => - rt.multipartBody[Any]( - value.map(part => - multipart(part.name, part.body) - .pipe(newPart => - part.headers.foldLeft(newPart) { case (acc, header) => - acc.header(header.name, header.value, true) - } - ) + for { + _ <- log.debug(s"Received headers: ${headers.keys.mkString(", ")}") + req = basicRequest + .headers(headers -- proxyConfig.excludedRequestHeaders) + .method(Method(method.entryName), requestUri) + .pipe(rt => + body match { + case AbsentRequestBody => rt + case SimpleRequestBody(value) => rt.body(value) + case MultipartRequestBody(value) => + rt.multipartBody[Any]( + value.map(part => + multipart(part.name, part.body) + .pipe(newPart => + part.headers.foldLeft(newPart) { case (acc, header) => + acc.header(header.name, header.value, true) + } + ) + ) ) - ) - } - ) - .response(asByteArrayAlways) - .readTimeout(timeout.getOrElse(1.minute.asScala)) - .send(httpBackend) - .map { response => - BinaryResponse( - response.code.code, - response.headers - .filterNot(h => proxyConfig.excludedResponseHeaders(h.name)) - .map(h => h.name -> h.value) - .toMap, - response.body.coerce[ByteArray], - delay + } ) - } + .response(asByteArrayAlways) + _ <- log.debug("Executing request: {}", req.toCurl).when(proxyConfig.logOutgoingRequests) + response <- req + .readTimeout(timeout.getOrElse(1.minute.asScala)) + .send(httpBackend) + } yield BinaryResponse( + response.code.code, + response.headers + .filterNot(h => proxyConfig.excludedResponseHeaders(h.name)) + .map(h => h.name -> h.value) + .toMap, + response.body.coerce[ByteArray], + delay + ) } private def jsonProxyRequest( @@ -202,43 +204,47 @@ final class PublicApiHandler( val requestUri = uri"$uri".pipe(query.foldLeft(_) { case (u, (key, value)) => u.addParam(key, value) }) - log.debug(s"Received headers: ${headers.keys.mkString(", ")}") *> basicRequest - .headers(headers -- proxyConfig.excludedRequestHeaders) - .method(Method(method.entryName), requestUri) - .pipe(rt => - body match { - case AbsentRequestBody => rt - case SimpleRequestBody(value) => rt.body(value) - case MultipartRequestBody(value) => - rt.multipartBody[Any]( - value.map(part => - multipart(part.name, part.body) - .pipe(newPart => - part.headers.foldLeft(newPart) { case (acc, header) => acc.header(header.name, header.value, true) } - ) + for { + _ <- log.debug(s"Received headers: ${headers.keys.mkString(", ")}") + req = basicRequest + .headers(headers -- proxyConfig.excludedRequestHeaders) + .method(Method(method.entryName), requestUri) + .pipe(rt => + body match { + case AbsentRequestBody => rt + case SimpleRequestBody(value) => rt.body(value) + case MultipartRequestBody(value) => + rt.multipartBody[Any]( + value.map(part => + multipart(part.name, part.body) + .pipe(newPart => + part.headers.foldLeft(newPart) { case (acc, header) => + acc.header(header.name, header.value, true) + } + ) + ) ) - ) - } - ) - .response(asJsonAlways[Json]) - .readTimeout(timeout.getOrElse(1.minute.asScala)) - .send(httpBackend) - .map { response => - response.body match { - case Right(jsonResponse) => - RawResponse( - response.code.code, - response.headers - .filterNot(h => proxyConfig.excludedResponseHeaders(h.name)) - .map(h => h.name -> h.value) - .toMap, - jsonResponse.patch(data, patch).noSpaces, - delay - ) - case Left(error) => - RawResponse(500, Map(), error.body, delay) - } - } + } + ) + .response(asJsonAlways[Json]) + _ <- log.debug("Executing request: {}", req.toCurl).when(proxyConfig.logOutgoingRequests) + response <- req + .readTimeout(timeout.getOrElse(1.minute.asScala)) + .send(httpBackend) + } yield response.body match { + case Right(jsonResponse) => + RawResponse( + response.code.code, + response.headers + .filterNot(h => proxyConfig.excludedResponseHeaders(h.name)) + .map(h => h.name -> h.value) + .toMap, + jsonResponse.patch(data, patch).noSpaces, + delay + ) + case Left(error) => + RawResponse(500, Map(), error.body, delay) + } } private def xmlProxyRequest( @@ -257,43 +263,47 @@ final class PublicApiHandler( val requestUri = uri"$uri".pipe(query.foldLeft(_) { case (u, (key, value)) => u.addParam(key, value) }) - log.debug(s"Received headers: ${headers.keys.mkString(", ")}") *> basicRequest - .headers(headers -- proxyConfig.excludedRequestHeaders) - .method(Method(method.entryName), requestUri) - .pipe(rt => - body match { - case AbsentRequestBody => rt - case SimpleRequestBody(value) => rt.body(value) - case MultipartRequestBody(value) => - rt.multipartBody[Any]( - value.map(part => - multipart(part.name, part.body) - .pipe(newPart => - part.headers.foldLeft(newPart) { case (acc, header) => acc.header(header.name, header.value, true) } - ) + for { + _ <- log.debug(s"Received headers: ${headers.keys.mkString(", ")}") + req = basicRequest + .headers(headers -- proxyConfig.excludedRequestHeaders) + .method(Method(method.entryName), requestUri) + .pipe(rt => + body match { + case AbsentRequestBody => rt + case SimpleRequestBody(value) => rt.body(value) + case MultipartRequestBody(value) => + rt.multipartBody[Any]( + value.map(part => + multipart(part.name, part.body) + .pipe(newPart => + part.headers.foldLeft(newPart) { case (acc, header) => + acc.header(header.name, header.value, true) + } + ) + ) ) - ) - } - ) - .response(asXML) - .readTimeout(timeout.getOrElse(1.minute.asScala)) - .send(httpBackend) - .map { response => - response.body match { - case Right(xmlResponse) => - RawResponse( - response.code.code, - response.headers - .filterNot(h => proxyConfig.excludedResponseHeaders(h.name)) - .map(h => h.name -> h.value) - .toMap, - xmlResponse.patchFromValues(jData, xData, patch.map { case (k, v) => k.toZoom -> v }).toString(), - delay - ) - case Left(error) => - RawResponse(500, Map(), error, delay) - } - } + } + ) + .response(asXML) + _ <- log.debug("Executing request: {}", req.toCurl).when(proxyConfig.logOutgoingRequests) + response <- req + .readTimeout(timeout.getOrElse(1.minute.asScala)) + .send(httpBackend) + } yield response.body match { + case Right(xmlResponse) => + RawResponse( + response.code.code, + response.headers + .filterNot(h => proxyConfig.excludedResponseHeaders(h.name)) + .map(h => h.name -> h.value) + .toMap, + xmlResponse.patchFromValues(jData, xData, patch.map { case (k, v) => k.toZoom -> v }).toString(), + delay + ) + case Left(error) => + RawResponse(500, Map(), error, delay) + } } } diff --git a/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/config/Configuration.scala b/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/config/Configuration.scala index 470351da..aedf3fba 100644 --- a/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/config/Configuration.scala +++ b/backend/mockingbird/src/main/scala/ru/tinkoff/tcb/mockingbird/config/Configuration.scala @@ -32,7 +32,8 @@ case class ProxyConfig( excludedRequestHeaders: Seq[String], excludedResponseHeaders: Set[String], proxyServer: Option[ProxyServerConfig], - insecureHosts: Seq[String] + insecureHosts: Seq[String], + logOutgoingRequests: Boolean ) case class EventConfig(fetchInterval: FiniteDuration, reloadInterval: FiniteDuration) diff --git a/configuration.md b/configuration.md index 8e1e216b..dcc16615 100644 --- a/configuration.md +++ b/configuration.md @@ -23,6 +23,7 @@ Mockingbird конфигурируется посредством файла sec "excludedRequestHeaders": [..], "excludedResponseHeaders": [..], "insecureHosts": [..], + "logOutgoingRequests": false, "proxyServer": { "type": "http" | "socks", "type": "..", @@ -87,14 +88,17 @@ healthCheckRoute - необязательный параметр, позволя "excludedResponseHeaders": ["transfer-encoding"], "insecureHosts": [ "some.host" - ] + ], + "logOutgoingRequests": false } } } ``` В поле insecureHosts можно указать список хостов, для которых не будет выполняться проверка сертификатов. Это может быть полезно -для случаев развёртывания во внутренней инфраструктуре +для случаев развёртывания во внутренней инфраструктуре. + +Флаг logOutgoingRequests позволяет включить логирование запросов к удаленному серверу, когда http заглушка работет в режиме прокси. Запрос пишется в лог в виде команды curl с заголовками и телом запроса. Так-же в этой секции можно указать настройки прокси сервера. Эти настройки влияют на ВСЕ http запросы, которые делаем mockingbird, т.е.: - запросы к внешнему серверу с proxy моках