Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-users] Streaming proxy of content using jetty and jetty-client with end to end flow control

Address list / Mirror list / fallback logic could be entirely within the HttpClient layer, proxy isn't involved there.


Joakim Erdfelt / joakim@xxxxxxxxxxx

On Wed, Sep 27, 2017 at 2:22 PM, Steven Schlansker <stevenschlansker@xxxxxxxxx> wrote:

> On Sep 26, 2017, at 5:05 PM, Joakim Erdfelt <joakim@xxxxxxxxxxx> wrote:
>
> modifying the request or response during proxy is also supported out of the box by the ...
> org.eclipse.jetty.proxy.AsyncMiddleManServlet
>
> https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java
>

This is getting much closer to what I need.  The big thing that's missing, we have a custom
service resolution protocol layered on top of DNS; I have a list of URIs to attempt
and if there is a failure before the request content is sent (for example, socket
timeout, no route to host, connection refused) I then want to try the next in the list
until we have a success or the operation times out.

(Once we write the request, we do not do further retries)

My initial attempt was to implement this as a custom HttpDestination subclass and
subclass of SocketAddressResolver.Async that returns many InetSocketAddresses;
unfortunately the resolver does not receive the Destination and similarly the
Request gets no knowledge of which address ends up getting picked.

I tried prying open the HttpConversation and HttpExchange objects by the usual blind downcasts,
but it was seemingly not possible to figure out the actual endpoint selected.

Not knowing the actual destination means we can't log all the information we want to.
So my current approach is the tried and true "while (keepTrying) { client.send(...); }"
which is less elegant but then we know the actual URI we try every time :)


The MiddleManServlet takes care of the HTTP proxying beautifully, and you are correct
that I'd rather not replicate that.  But it doesn't look like it does this sort of
retries, and I'm not sure I can plug in to all the right places to collect the metrics
and logging data I need.  I'll play with it though and maybe with some cleverly placed
overrides I can get most of it.

>
>
> Joakim Erdfelt / joakim@xxxxxxxxxxx
>
> On Tue, Sep 26, 2017 at 5:03 PM, Steven Schlansker <stevenschlansker@xxxxxxxxx> wrote:
>
> > On Sep 26, 2017, at 4:51 PM, Joakim Erdfelt <joakim@xxxxxxxxxxx> wrote:
> >
> > This is what our org.eclipse.jetty.proxy.AsyncProxyServlet already does.
> >
> > https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
> >
> > Why write your own?
>
> Because I did not know this existed :)
>
> Also, I do nontrivial rewriting of the request (this is not at all a transparent proxy,
> it is quite invasive) so I will probably not be able to use the code as is.
>
> But it probably answers my questions and just needs adaptation, so thank you very much
> for the pointer.
>
> (This is a lot of complex code!  Asynchronous programming is hard...)
>
> >
> >
> > Joakim Erdfelt / joakim@xxxxxxxxxxx
> >
> > On Tue, Sep 26, 2017 at 4:47 PM, Steven Schlansker <stevenschlansker@xxxxxxxxx> wrote:
> > Hi jetty-users,
> >
> > Still working on my http proxy :)
> > Now I'm trying to make sure I've got the content proxying working correctly
> > using both asynchronous servlet as well as NIO nonblocking writing.
> >
> > Because both the server and client are pushing notifications at me, I believe
> > I need to implement some sort of transfer queue.  I was hoping that I'd end up
> > in a push-pull situation but ServletOutputStream calls you via a WriteListener
> > and Jetty-Client calls you via a Response.ContentListener.
> >
> > Unfortunately, this leaves me in a position where I don't know how to get flow
> > control working.  The client pushes data at me and I don't have a way to push back.
> >
> > My current pesudocoded approach:
> >
> > Deque<Runnable> writes;
> > volatile boolean complete = false;
> >
> > asyncCtx = httpResponse.startAsync();
> > out = (HttpOutput) httpResponse.getOutputStream();
> >
> > writeListener = new WriteListener() {
> >     synchronized void onWritePossible() { // avoid re-entrance with 'synchronized'
> >         while(out.isReady()) {
> >             pending = writes.removeFirst();
> >             if (pending != null) {
> >                 pending.run();
> >             } else if (complete && writes.isEmpty()) {
> >                 asyncCtx.complete();
> >             } else {
> >                 return;
> >             }
> >         }
> >     }
> > };
> >
> > out.setWriteListener(writeListener);
> >
> > proxyClientRequest.send(new Response.ContentListener() {
> >     void onContent(ByteBuffer buf, Callback complete) {
> >         writes.push(() -> {
> >             out.write(buf);
> >             complete.succeeded();
> >         };
> >         writeListener.onWritePossible();  // See if we can write it immediately
> >     }
> >
> >     void onComplete() {
> >         complete = true;
> >         writeListener.onWritePossible();
> >     }
> > });
> >
> > This has the nice property where I don't copy buffers, just keep them in a queue
> > of pending writes -- but I worry that in the case of a slow reader, Jetty Client
> > will continue onContent-pushing buffers at me until I run out of memory.
> >
> > I could instead use the OutputStreamContentProvider and just read from it in the
> > WriteListener, which might fix the flow control, but that seems likely to involve
> > a number of byte[] copies that I was hoping to avoid.
> >
> > What's the right approach to wire up Jetty server to Jetty client with full NIO + Async
> > content proxying, without introducing an intermediate transfer buffer that might grow
> > without bound?  Am I on the right path or am I missing something?  Thanks for any guidance!
> >
> >
> > _______________________________________________
> > jetty-users mailing list
> > jetty-users@xxxxxxxxxxx
> > To change your delivery options, retrieve your password, or unsubscribe from this list, visit
> > https://dev.eclipse.org/mailman/listinfo/jetty-users
> >
> > _______________________________________________
> > jetty-users mailing list
> > jetty-users@xxxxxxxxxxx
> > To change your delivery options, retrieve your password, or unsubscribe from this list, visit
> > https://dev.eclipse.org/mailman/listinfo/jetty-users
>
>
> _______________________________________________
> jetty-users mailing list
> jetty-users@xxxxxxxxxxx
> To change your delivery options, retrieve your password, or unsubscribe from this list, visit
> https://dev.eclipse.org/mailman/listinfo/jetty-users
>
> _______________________________________________
> jetty-users mailing list
> jetty-users@xxxxxxxxxxx
> To change your delivery options, retrieve your password, or unsubscribe from this list, visit
> https://dev.eclipse.org/mailman/listinfo/jetty-users


_______________________________________________
jetty-users mailing list
jetty-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/jetty-users


Back to the top