org.cometd.client.http.jetty.JettyHttpClientTransport Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cometd.client.http.jetty;
import java.net.HttpCookie;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.Promise;
import org.cometd.client.http.common.AbstractHttpClientTransport;
import org.cometd.client.transport.ClientTransport;
import org.cometd.client.transport.TransportListener;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
public class JettyHttpClientTransport extends AbstractHttpClientTransport {
private final HttpClient _httpClient;
private final List _requests = new ArrayList<>();
public JettyHttpClientTransport(Map options, HttpClient httpClient) {
this(null, options, httpClient);
}
public JettyHttpClientTransport(String url, Map options, HttpClient httpClient) {
super(url, options);
_httpClient = httpClient;
}
protected HttpClient getHttpClient() {
return _httpClient;
}
@Override
public void init() {
super.init();
long defaultMaxNetworkDelay = getHttpClient().getIdleTimeout();
if (defaultMaxNetworkDelay <= 0) {
defaultMaxNetworkDelay = 10000;
}
setMaxNetworkDelay(defaultMaxNetworkDelay);
}
@Override
public void abort(Throwable failure) {
List requests;
synchronized (this) {
super.abort(failure);
requests = new ArrayList<>(_requests);
_requests.clear();
}
for (Request request : requests) {
request.abort(failure);
}
}
@Override
public void send(final TransportListener listener, final List messages) {
String requestURI = newRequestURI(messages);
final Request request = _httpClient.newRequest(requestURI).method(HttpMethod.POST);
request.header(HttpHeader.CONTENT_TYPE.asString(), "application/json;charset=UTF-8");
URI cookieURI = URI.create(getURL());
List cookies = getCookies(cookieURI);
StringBuilder value = new StringBuilder(cookies.size() * 32);
for (HttpCookie cookie : cookies) {
if (value.length() > 0) {
value.append("; ");
}
value.append(cookie.getName()).append("=").append(cookie.getValue());
}
request.header(HttpHeader.COOKIE.asString(), value.toString());
request.content(new StringContentProvider(generateJSON(messages)));
customize(request, Promise.from(
customizedRequest -> send(listener, messages, cookieURI, customizedRequest),
error -> listener.onFailure(error, messages)
));
}
private void send(TransportListener listener, List messages, URI cookieURI, Request request) {
request.listener(new Request.Listener.Adapter() {
@Override
public void onHeaders(Request request) {
listener.onSending(messages);
}
});
long maxNetworkDelay = calculateMaxNetworkDelay(messages);
// Set the idle timeout for this request larger than the total
// timeout so there are no races between the two timeouts.
request.idleTimeout(maxNetworkDelay * 2, TimeUnit.MILLISECONDS);
request.timeout(maxNetworkDelay, TimeUnit.MILLISECONDS);
synchronized (this) {
if (!isAborted()) {
_requests.add(request);
}
}
request.send(new BufferingResponseListener(getMaxMessageSize()) {
@Override
public boolean onHeader(Response response, HttpField field) {
if (response.getStatus() == HttpStatus.OK_200) {
HttpHeader header = field.getHeader();
if (header == HttpHeader.SET_COOKIE || header == HttpHeader.SET_COOKIE2) {
// We do not allow cookies to be handled by HttpClient, since one
// HttpClient instance is shared by multiple BayeuxClient instances.
// Instead, we store the cookies in the BayeuxClient instance.
Map> cookies = new HashMap<>(1);
cookies.put(field.getName(), Collections.singletonList(field.getValue()));
storeCookies(cookieURI, cookies);
return false;
}
}
return true;
}
@Override
public void onComplete(Result result) {
synchronized (JettyHttpClientTransport.this) {
_requests.remove(result.getRequest());
}
if (result.isFailed()) {
listener.onFailure(result.getFailure(), messages);
return;
}
Response response = result.getResponse();
int status = response.getStatus();
if (status == HttpStatus.OK_200) {
processResponseContent(listener, messages, getContentAsString());
} else {
processWrongResponseCode(listener, messages, status);
}
}
});
}
protected void customize(Request request) {
}
protected void customize(Request request, Promise promise) {
try {
customize(request);
promise.succeed(request);
} catch (Throwable x) {
promise.fail(x);
}
}
public static class Factory extends ContainerLifeCycle implements ClientTransport.Factory {
private final HttpClient httpClient;
public Factory(HttpClient httpClient) {
this.httpClient = httpClient;
addBean(httpClient);
}
@Override
public ClientTransport newClientTransport(String url, Map options) {
return new JettyHttpClientTransport(url, options, httpClient);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy