@Override public boolean isEmpty() { return delegate.isEmpty(); }
@Override public String toString() { final ToStringHelper helper = MoreObjects.toStringHelper(this); if (!informationals().isEmpty()) { helper.add("informationals", informationals()); } helper.add("headers", headers()) .add("content", content()); if (!trailingHeaders().isEmpty()) { helper.add("trailingHandlers", trailingHeaders()); } return helper.toString(); }
/** * Returns {@code true} if the content of the response with the given {@link HttpStatus} is expected to * be always empty (1xx, 204, 205 and 304 responses.) * * @throws IllegalArgumentException if the specified {@code content} or {@code trailingHeaders} are * non-empty when the content is always empty */ public static boolean isContentAlwaysEmptyWithValidation( HttpStatus status, HttpData content, HttpHeaders trailingHeaders) { if (!isContentAlwaysEmpty(status)) { return false; } if (!content.isEmpty()) { throw new IllegalArgumentException( "A " + status + " response must have empty content: " + content.length() + " byte(s)"); } if (!trailingHeaders.isEmpty()) { throw new IllegalArgumentException( "A " + status + " response must not have trailing headers: " + trailingHeaders); } return true; }
@Override public void onComplete() { if (!writer.isOpen()) { return; } if (!trailingHeaders.isEmpty()) { writer.write(trailingHeaders); } writer.close(); } }
private LastHttpContent convertTrailingHeaders(int streamId, HttpHeaders headers) throws Http2Exception { final LastHttpContent lastContent; if (headers.isEmpty()) { lastContent = LastHttpContent.EMPTY_LAST_CONTENT; } else { lastContent = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, false); convert(streamId, headers, lastContent.trailingHeaders(), true, false); } return lastContent; }
private static void convertHeaders(HttpHeaders headers, MimeHeaders cHeaders) { if (headers.isEmpty()) { return; } for (Entry<AsciiString, String> e : headers) { final AsciiString k = e.getKey(); final String v = e.getValue(); if (k.isEmpty() || k.byteAt(0) == ':') { continue; } final MessageBytes cValue = cHeaders.addValue(k.array(), k.arrayOffset(), k.length()); final byte[] valueBytes = v.getBytes(StandardCharsets.US_ASCII); cValue.setBytes(valueBytes, 0, valueBytes.length); } }
/** * Converts the {@link AggregatedHttpMessage} into a new complete {@link HttpResponse}. */ static HttpResponse of(AggregatedHttpMessage res) { requireNonNull(res, "res"); final List<HttpHeaders> informationals = res.informationals(); final HttpHeaders headers = res.headers(); final HttpData content = res.content(); final HttpHeaders trailingHeaders = res.trailingHeaders(); if (informationals.isEmpty()) { return of(headers, content, trailingHeaders); } final int numObjects = informationals.size() + 1 /* headers */ + (!content.isEmpty() ? 1 : 0) + (!trailingHeaders.isEmpty() ? 1 : 0); final HttpObject[] objs = new HttpObject[numObjects]; int writerIndex = 0; for (HttpHeaders informational : informationals) { objs[writerIndex++] = informational; } objs[writerIndex++] = headers; if (!content.isEmpty()) { objs[writerIndex++] = content; } if (!trailingHeaders.isEmpty()) { objs[writerIndex] = trailingHeaders; } return new RegularFixedHttpResponse(objs); }
/** * Creates a new HTTP response of the specified objects and closes the stream. */ static HttpResponse of(HttpHeaders headers, HttpData content, HttpHeaders trailingHeaders) { requireNonNull(headers, "headers"); requireNonNull(content, "content"); requireNonNull(trailingHeaders, "trailingHeaders"); final HttpStatus status = headers.status(); // From the section 8.1.2.4 of RFC 7540: //// For HTTP/2 responses, a single :status pseudo-header field is defined that carries the HTTP status //// code field (see [RFC7231], Section 6). This pseudo-header field MUST be included in all responses; //// otherwise, the response is malformed (Section 8.1.2.6). if (status == null) { throw new IllegalStateException("not a response (missing :status)"); } final HttpHeaders newHeaders = setOrRemoveContentLength(headers, content, trailingHeaders); if (content.isEmpty() && trailingHeaders.isEmpty()) { ReferenceCountUtil.safeRelease(content); return new OneElementFixedHttpResponse(newHeaders); } if (!content.isEmpty()) { if (trailingHeaders.isEmpty()) { return new TwoElementFixedHttpResponse(newHeaders, content); } else { return new RegularFixedHttpResponse(newHeaders, content, trailingHeaders); } } return new TwoElementFixedHttpResponse(newHeaders, trailingHeaders); }
/** * Creates a new instance. * * @param sessionProtocol the {@link SessionProtocol} of the invocation * @param request the request associated with this context */ public DefaultClientRequestContext( EventLoop eventLoop, MeterRegistry meterRegistry, SessionProtocol sessionProtocol, Endpoint endpoint, HttpMethod method, String path, @Nullable String query, @Nullable String fragment, ClientOptions options, Request request) { super(meterRegistry, sessionProtocol, method, path, query, request); this.eventLoop = requireNonNull(eventLoop, "eventLoop"); this.options = requireNonNull(options, "options"); this.endpoint = requireNonNull(endpoint, "endpoint"); this.fragment = fragment; log = new DefaultRequestLog(this); writeTimeoutMillis = options.defaultWriteTimeoutMillis(); responseTimeoutMillis = options.defaultResponseTimeoutMillis(); maxResponseLength = options.defaultMaxResponseLength(); final HttpHeaders headers = options.getOrElse(ClientOption.HTTP_HEADERS, HttpHeaders.EMPTY_HEADERS); if (!headers.isEmpty()) { createAdditionalHeadersIfAbsent().set(headers); } runThreadLocalContextCustomizer(); }
@Override public ServiceRequestContext newDerivedContext(Request request) { final DefaultServiceRequestContext ctx = new DefaultServiceRequestContext( cfg, ch, meterRegistry(), sessionProtocol(), pathMappingContext, pathMappingResult, (HttpRequest) request, sslSession(), proxiedAddresses(), clientAddress); final HttpHeaders additionalHeaders = additionalResponseHeaders(); if (!additionalHeaders.isEmpty()) { ctx.setAdditionalResponseHeaders(additionalHeaders); } final HttpHeaders additionalTrailers = additionalResponseTrailers(); if (!additionalTrailers.isEmpty()) { ctx.setAdditionalResponseTrailers(additionalTrailers); } for (final Iterator<Attribute<?>> i = attrs(); i.hasNext();/* noop */) { ctx.addAttr(i.next()); } return ctx; }
private static HttpResponseWriter aggregateFrom(CompletableFuture<?> future, HttpHeaders headers, HttpHeaders trailingHeaders, Function<Object, HttpData> contentConverter) { final HttpResponseWriter writer = HttpResponse.streaming(); future.handle((result, cause) -> { if (cause != null) { writer.close(cause); return null; } try { final HttpData content = contentConverter.apply(result); writer.write(headers); writer.write(content); if (!trailingHeaders.isEmpty()) { writer.write(trailingHeaders); } writer.close(); } catch (Exception e) { writer.close(e); } return null; }); return writer; }
private static HttpHeaders fillAdditionalHeaders(HttpHeaders headers, HttpHeaders additionalHeaders) { if (!additionalHeaders.isEmpty()) { if (headers.isImmutable()) { // All headers are already validated. final HttpHeaders temp = headers; headers = new DefaultHttpHeaders(false, temp.size() + additionalHeaders.size()); headers.set(temp); } headers.setAllIfAbsent(additionalHeaders); } return headers; }
if (!additionalHeaders.isEmpty()) { requestHeaders.setAllIfAbsent(additionalHeaders);
if (!trailingHeaders.isEmpty()) { write(trailingHeaders);
private HttpHeaders addCommonHeaders(HttpHeaders headers, HttpFileAttributes attrs, @Nullable String etag) { if (contentType != null) { headers.set(HttpHeaderNames.CONTENT_TYPE, contentType.toString()); } if (dateEnabled) { headers.setTimeMillis(HttpHeaderNames.DATE, clock.millis()); } if (lastModifiedEnabled) { headers.setTimeMillis(HttpHeaderNames.LAST_MODIFIED, attrs.lastModifiedMillis()); } if (etag != null) { headers.set(HttpHeaderNames.ETAG, '\"' + etag + '\"'); } if (!this.headers.isEmpty()) { headers.setAll(this.headers); } return headers; }
@Override public void onComplete() { if (!cancelTimeout() && reqCtx.requestTimeoutHandler() == null) { // We have already returned a failed response due to a timeout. return; } if (wroteNothing(state)) { logger.warn("{} Published nothing (or only informational responses): {}", ctx.channel(), service()); responseEncoder.writeReset(req.id(), req.streamId(), Http2Error.INTERNAL_ERROR); return; } if (state != State.DONE) { final HttpHeaders additionalTrailers = reqCtx.additionalResponseTrailers(); if (!additionalTrailers.isEmpty()) { write(additionalTrailers, true); } else { write(HttpData.EMPTY_DATA, true); } } }
if (!trailingHeaders.isEmpty()) { write(trailingHeaders);
private DefaultClientRequestContext(DefaultClientRequestContext ctx, Request request) { super(ctx.meterRegistry(), ctx.sessionProtocol(), ctx.method(), ctx.path(), ctx.query(), request); eventLoop = ctx.eventLoop(); options = ctx.options(); endpoint = ctx.endpoint(); fragment = ctx.fragment(); log = new DefaultRequestLog(this); writeTimeoutMillis = ctx.writeTimeoutMillis(); responseTimeoutMillis = ctx.responseTimeoutMillis(); maxResponseLength = ctx.maxResponseLength(); final HttpHeaders additionalHeaders = ctx.additionalRequestHeaders(); if (!additionalHeaders.isEmpty()) { createAdditionalHeadersIfAbsent().set(additionalHeaders); } for (final Iterator<Attribute<?>> i = ctx.attrs(); i.hasNext();) { addAttr(i.next()); } runThreadLocalContextCustomizer(); }