Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ClientRequestContextBuilder and ServiceRequestContextBuilder #1548

Merged
merged 6 commits into from
Jan 28, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -16,6 +16,9 @@

package com.linecorp.armeria.client;

import static java.util.Objects.requireNonNull;

import java.net.URI;
import java.time.Duration;

import javax.annotation.Nullable;
Expand All @@ -26,6 +29,7 @@
import com.linecorp.armeria.common.Request;
import com.linecorp.armeria.common.RequestContext;
import com.linecorp.armeria.common.Response;
import com.linecorp.armeria.common.RpcRequest;

import io.netty.handler.codec.Headers;
import io.netty.util.AsciiString;
Expand All @@ -36,6 +40,42 @@
*/
public interface ClientRequestContext extends RequestContext {

/**
* Returns a new {@link ClientRequestContext} created from the specified {@link HttpRequest}.
* Note that it is not usually required to create a new context by yourself, because Armeria
trustin marked this conversation as resolved.
Show resolved Hide resolved
* will always provide a context object for you. However, it may be useful in some cases such as
* unit testing.
*
* @see ClientRequestContextBuilder
*/
static ClientRequestContext of(HttpRequest request) {
return ClientRequestContextBuilder.of(request).build();
}

/**
* Returns a new {@link ClientRequestContext} created from the specified {@link RpcRequest} and URI.
* Note that it is not usually required to create a new context by yourself, because Armeria
* will always provide a context object for you. However, it may be useful in some cases such as
* unit testing.
*
* @see ClientRequestContextBuilder
*/
static ClientRequestContext of(RpcRequest request, String uri) {
return ClientRequestContextBuilder.of(request, URI.create(requireNonNull(uri, "uri"))).build();
}

/**
* Returns a new {@link ClientRequestContext} created from the specified {@link RpcRequest} and {@link URI}.
* Note that it is not usually required to create a new context by yourself, because Armeria
* will always provide a context object for you. However, it may be useful in some cases such as
* unit testing.
*
* @see ClientRequestContextBuilder
*/
static ClientRequestContext of(RpcRequest request, URI uri) {
return ClientRequestContextBuilder.of(request, uri).build();
}

@Override
ClientRequestContext newDerivedContext();

Expand Down
@@ -0,0 +1,126 @@
/*
* Copyright 2019 LINE Corporation
*
* LINE Corporation licenses this file to you 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:
*
* https://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 com.linecorp.armeria.client;

import static java.util.Objects.requireNonNull;

import java.net.URI;

import javax.annotation.Nullable;

import com.linecorp.armeria.common.AbstractRequestContextBuilder;
import com.linecorp.armeria.common.HttpMethod;
import com.linecorp.armeria.common.HttpRequest;
import com.linecorp.armeria.common.RpcRequest;

/**
* Builds a new {@link ClientRequestContext}. Note that it is not usually required to create a new context by
* yourself, because Armeria will always provide a context object for you. However, it may be useful in some
* cases such as unit testing.
*/
public final class ClientRequestContextBuilder
extends AbstractRequestContextBuilder<ClientRequestContextBuilder> {

/**
* Returns a new {@link ClientRequestContextBuilder} created from the specified {@link HttpRequest}.
*/
public static ClientRequestContextBuilder of(HttpRequest request) {
return new ClientRequestContextBuilder(request);
}

/**
* Returns a new {@link ClientRequestContextBuilder} created from the specified {@link RpcRequest} and URI.
*/
public static ClientRequestContextBuilder of(RpcRequest request, String uri) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does ClientRequestContextBuilder accept RpcRequest but not ServiceRequestContextBuilder? If the use case is for a server that processes messages from Kafka, for example, it seems like a service (the client is whoever published the message).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a ServiceRequestContext's request has been always an HttpRequest? For instance, ServiceRequestContext.service() always returns Service<HttpRequest, HttpResponse>. We may want to change this if we support more than HTTP but I guess it would be hard to occur at the moment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case what would it look like to model the kafka request with ServiceRequestContext? Would it need a dummy HTTP request? I'm trying to understand how this will solve the use case, at first I expected to create a ServiceRequestContext with RpcRequest.of(KafkaService.class or something

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a dummy HTTP request will be required. A user may want to set some custom attributes as well.

return of(request, URI.create(requireNonNull(uri, "uri")));
}

/**
* Returns a new {@link ClientRequestContextBuilder} created from the specified {@link RpcRequest} and
* {@link URI}.
*/
public static ClientRequestContextBuilder of(RpcRequest request, URI uri) {
return new ClientRequestContextBuilder(request, uri);
}

@Nullable
private final String fragment;
@Nullable
private Endpoint endpoint;
private ClientOptions options = ClientOptions.DEFAULT;

private ClientRequestContextBuilder(HttpRequest request) {
super(false, request);
fragment = null;
}

private ClientRequestContextBuilder(RpcRequest request, URI uri) {
super(false, request, uri);
fragment = uri.getRawFragment();
}

@Override
public ClientRequestContextBuilder method(HttpMethod method) {
super.method(method);
return this;
}

/**
* Sets the {@link Endpoint} of the request. If not set, it is auto-generated from the request authority.
*/
public ClientRequestContextBuilder endpoint(Endpoint endpoint) {
this.endpoint = requireNonNull(endpoint, "endpoint");
return this;
}

/**
* Sets the {@link ClientOptions} of the client. If not set, {@link ClientOptions#DEFAULT} is used.
*/
public ClientRequestContextBuilder options(ClientOptions options) {
this.options = requireNonNull(options, "options");
return this;
}

/**
* Returns a new {@link ClientRequestContext} created with the properties of this builder.
*/
public ClientRequestContext build() {
final Endpoint endpoint;
if (this.endpoint != null) {
endpoint = this.endpoint;
} else {
endpoint = Endpoint.parse(authority());
}

final DefaultClientRequestContext ctx = new DefaultClientRequestContext(
eventLoop(), meterRegistry(), sessionProtocol(), endpoint,
method(), path(), query(), fragment, options, request());

if (isRequestStartTimeSet()) {
ctx.logBuilder().startRequest(fakeChannel(), sessionProtocol(), sslSession(),
requestStartTimeNanos(), requestStartTimeMicros());
} else {
ctx.logBuilder().startRequest(fakeChannel(), sessionProtocol(), sslSession());
}

if (request() instanceof HttpRequest) {
ctx.logBuilder().requestHeaders(((HttpRequest) request()).headers());
} else {
ctx.logBuilder().requestContent(request(), null);
}
return ctx;
}
}
Expand Up @@ -23,6 +23,7 @@
import java.util.function.Consumer;

import javax.annotation.Nullable;
import javax.net.ssl.SSLSession;

import com.linecorp.armeria.common.DefaultHttpHeaders;
import com.linecorp.armeria.common.HttpHeaders;
Expand Down Expand Up @@ -178,6 +179,16 @@ public EventLoop eventLoop() {
return eventLoop;
}

@Nullable
@Override
public SSLSession sslSession() {
if (log.isAvailable(RequestLogAvailability.REQUEST_START)) {
return log.sslSession();
} else {
return null;
}
}

@Override
public ClientOptions options() {
return options;
Expand Down
Expand Up @@ -32,7 +32,7 @@ public final class UnprocessedRequestException extends RuntimeException {
private static final UnprocessedRequestException INSTANCE = new UnprocessedRequestException(false);

/**
* Returns a {@link UnprocessedRequestException} which may be a singleton or a new instance, depending on
* Returns an {@link UnprocessedRequestException} which may be a singleton or a new instance, depending on
* whether {@linkplain Flags#verboseExceptions() the verbose exception mode} is enabled.
*/
public static UnprocessedRequestException get() {
Expand Down