Ich versuche, einen umgekehrten HTTP-Proxy mit Spray/Akka zu implementieren, aber läuft in Schwierigkeiten. Ich habe festgestellt, dass mein Proxy-Server unter bestimmten Umständen weiterhin Daten vom Upstream-Server empfängt, selbst nachdem der Client die Verbindung getrennt hat.Spray Reverse Proxy: Daten weiterleiten, nachdem der Client die Verbindung getrennt hat
Hier ist, wie ich meine Spray-Proxy-Richtlinie umzusetzen (nur eine wenig Änderung bthuillier's implementation):
trait ProxyDirectives {
private def sending(f: RequestContext ⇒ HttpRequest)(implicit system: ActorSystem): Route = {
val transport = IO(Http)(system)
ctx ⇒ transport.tell(f(ctx), ctx.responder)
}
/**
* Re-shape the original request, to match the destination server.
*/
private def reShapeRequest(req: HttpRequest, uri: Uri): HttpRequest = {
req.copy(
uri = uri,
headers = req.headers.map {
case x: HttpHeaders.Host => HttpHeaders.Host(uri.authority.host.address, uri.authority.port)
case x => x
}
)
}
/**
* proxy the request to the specified uri
*
*/
def proxyTo(uri: Uri)(implicit system: ActorSystem): Route = {
sending(ctx => reShapeRequest(ctx.request, uri))
}
}
Dieser Reverse-Proxy wird gut funktionieren, wenn ich eine Proxy-Schicht zwischen dem Client setzen und dem Server (das heißt, Client < -> proxyTo < -> Server), aber es wird Probleme haben, wenn ich zwei Schichten zwischen dem Client und dem Server. Zum Beispiel, wenn ich habe den folgenden einfachen Python HTTP-Server erhalten:
import socket
from threading import Thread, Semaphore
import time
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
class MyHTTPHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
self.send_response(200)
self.send_header('Transfer-Encoding', 'chunked')
self.end_headers()
for i in range(100):
data = ('%s\n' % i).encode('utf-8')
self.wfile.write(hex(len(data))[2:].encode('utf-8'))
self.wfile.write(b'\r\n')
self.wfile.write(data)
self.wfile.write(b'\r\n')
time.sleep(1)
self.wfile.write(b'0\r\n\r\n')
class MyServer(ThreadingMixIn, HTTPServer):
def server_bind(self):
HTTPServer.server_bind(self)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
def server_close(self):
HTTPServer.server_close(self)
if __name__ == '__main__':
server = MyServer(('127.0.0.1', 8080), MyHTTPHandler)
server.serve_forever()
die im Grunde nichts tut, sondern eine segmentierte Antwort öffnen (für den Lauf langfristigen, damit können wir die Fragen Prüfung). Und wenn ich die Kette zwei Schichten von Proxies auf folgende Weise:
class TestActor(val target: String)(implicit val system: ActorSystem) extends Actor
with HttpService
with ProxyDirectives
{
// we use the enclosing ActorContext's or ActorSystem's dispatcher for our Futures and Scheduler
implicit private def executionContext = actorRefFactory.dispatcher
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
val serviceRoute: Route = {
get {
proxyTo(target)
}
}
// runs the service routes.
def receive = runRoute(serviceRoute) orElse handleTimeouts
private def handleTimeouts: Receive = {
case Timedout(x: HttpRequest) =>
sender ! HttpResponse(StatusCodes.InternalServerError, "Request timed out.")
}
}
object DebugMain extends App {
val actorName = "TestActor"
implicit val system = ActorSystem(actorName)
// create and start our service actor
val service = system.actorOf(
Props { new TestActor("http://127.0.0.1:8080") },
s"${actorName}Service"
)
val service2 = system.actorOf(
Props { new TestActor("http://127.0.0.1:8081") },
s"${actorName}2Service"
)
IO(Http) ! Http.Bind(service, "::0", port = 8081)
IO(Http) ! Http.Bind(service2, "::0", port = 8082)
}
Verwenden curl http://localhost:8082
an den Proxy-Server verbinden, und Sie werden das Akka System hält Daten sehen die Übertragung auch nach curl getötet wurde (möglicherweise einschalten die Protokolle der DEBUG-Ebene, um Details zu sehen).
Wie kann ich mit diesem Problem umgehen? Vielen Dank.