@Override public boolean sendMessageToEndPoint(final HttpMessage message) { Assert.notNull(this.httpClient); try { final HttpPost request = new HttpPost(message.getUrl().toURI()); request.addHeader("Content-Type", message.getContentType()); final StringEntity entity = new StringEntity(message.getMessage(), ContentType.create(message.getContentType())); request.setEntity(entity); final ResponseHandler<Boolean> handler = response -> response.getStatusLine().getStatusCode() == HttpStatus.SC_OK; final HttpRequestFutureTask<Boolean> task = this.requestExecutorService.execute(request, HttpClientContext.create(), handler); if (message.isAsynchronous()) { return true; } return task.get(); } catch (final RejectedExecutionException e) { LOGGER.warn(e.getMessage(), e); return false; } catch (final Exception e) { LOGGER.debug(e.getMessage(), e); return false; } }
/** * Sets or removes the {@code "Expect: 100-continue"} header to / from the specified message. If * the specified {@code value} is {@code true}, the {@code "Expect: 100-continue"} header is set * and all other previous {@code "Expect"} headers are removed. Otherwise, all {@code "Expect"} * headers are removed completely. */ public static void set100ContinueExpected(HttpMessage message, boolean set) { if (set) { message.headers().set(Names.EXPECT, Values.CONTINUE); } else { message.headers().remove(Names.EXPECT); } }
/** Sets the {@code "Date"} header. */ public static void setDate(HttpMessage message, Date value) { if (value != null) { message.headers().set(Names.DATE, HttpHeaderDateFormat.get().format(value)); } else { message.headers().set(Names.DATE, null); } }
/** * Sets a new date header with the specified name and value. If there is an existing header with * the same name, the existing header is removed. The specified value is formatted as defined in * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a> */ public static void setDateHeader(HttpMessage message, CharSequence name, Date value) { if (value != null) { message.headers().set(name, HttpHeaderDateFormat.get().format(value)); } else { message.headers().set(name, null); } }
public static void checkRequestHasContentLengthOrChunkedEncoding( HttpMessage request, String message) { boolean chunked = "chunked".equals(request.getFirstHeaderOrNull("Transfer-Encoding")); checkArgument( request.getPayload() == null || chunked || request.getPayload().getContentMetadata().getContentLength() != null, message); }
public static Long attemptToParseSizeAndRangeFromHeaders(HttpMessage from) throws HttpException { String contentRange = from.getFirstHeaderOrNull("Content-Range"); if (contentRange == null && from.getPayload() != null) { return from.getPayload().getContentMetadata().getContentLength(); } else if (contentRange != null) { return Long.parseLong(contentRange.substring(contentRange.lastIndexOf('/') + 1)); } return null; }
/** * Returns {@code true} if and only if the connection can remain open and thus 'kept alive'. This * methods respects the value of the {@code "Connection"} header first and then the return value * of {@link HttpVersion#isKeepAliveDefault()}. */ public static boolean isKeepAlive(HttpMessage message) { String connection = message.headers().get(Names.CONNECTION); if (connection != null && equalsIgnoreCase(Values.CLOSE, connection)) { return false; } if (message.getProtocolVersion().isKeepAliveDefault()) { return !equalsIgnoreCase(Values.CLOSE, connection); } else { return equalsIgnoreCase(Values.KEEP_ALIVE, connection); } }
/** * Returns the header value with the specified header name. If there are more than one header * value for the specified header name, the first value is returned. * * @return the header value or the {@code defaultValue} if there is no such header */ public static String getHeader(HttpMessage message, CharSequence name, String defaultValue) { String value = message.headers().get(name); if (value == null) { return defaultValue; } return value; }
/** * Sets the value of the {@code "Connection"} header depending on the protocol version of the * specified message. This getMethod sets or removes the {@code "Connection"} header depending on * what the default keep alive mode of the message's protocol version is, as specified by {@link * HttpVersion#isKeepAliveDefault()}. * * <ul> * <li>If the connection is kept alive by default: * <ul> * <li>set to {@code "close"} if {@code keepAlive} is {@code false}. * <li>remove otherwise. * </ul> * <li>If the connection is closed by default: * <ul> * <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}. * <li>remove otherwise. * </ul> * </ul> */ public static void setKeepAlive(HttpMessage message, boolean keepAlive) { HttpHeaders h = message.headers(); if (message.getProtocolVersion().isKeepAliveDefault()) { if (keepAlive) { h.remove(Names.CONNECTION); } else { h.set(Names.CONNECTION, Values.CLOSE); } } else { if (keepAlive) { h.set(Names.CONNECTION, Values.KEEP_ALIVE); } else { h.remove(Names.CONNECTION); } } }
private OAuthAccessToken parseUrlEncodedToken(HttpMessage response) { Map<String, String[]> params = new HashMap<String, String[]>(); AuthUtils.parseFormUrlEncoded(response, params); if (response.getStatus() == 200) { String accessTokenE = AuthUtils.getParamValue(params, "access_token"); if (accessTokenE != null) { String accessToken = accessTokenE; WDate expires = null; String expiresE = AuthUtils.getParamValue(params, "expires"); if (expiresE != null) { expires = new WDate(new Date()).addSeconds(Integer.parseInt(expiresE)); } return new OAuthAccessToken(accessToken, expires, ""); } else { throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService.badresponse")); } } else { String errorE = AuthUtils.getParamValue(params, "error"); if (errorE != null) { throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService." + errorE)); } else { throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService.badresponse")); } } }
public static void wirePayloadIfEnabled(Wire wire, HttpMessage request) { if (request.getPayload() != null && wire.enabled()) { wire.output(request); checkRequestHasContentLengthOrChunkedEncoding( request, "After wiring, the request has neither chunked encoding nor content length: " + request); } }
public static void removeTransferEncodingChunked(HttpMessage m) { List<String> values = m.headers().getAll(Names.TRANSFER_ENCODING); if (values.isEmpty()) { return; } Iterator<String> valuesIt = values.iterator(); while (valuesIt.hasNext()) { String value = valuesIt.next(); if (equalsIgnoreCase(value, Values.CHUNKED)) { valuesIt.remove(); } } if (values.isEmpty()) { m.headers().remove(Names.TRANSFER_ENCODING); } else { m.headers().set(Names.TRANSFER_ENCODING, values); } }
@Override protected Object decode(ChannelHandlerContext ctx, HttpMessage msg) throws Exception { String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING); if (acceptedEncoding == null) { acceptedEncoding = HttpHeaders.Values.IDENTITY; } boolean offered = acceptEncodingQueue.offer(acceptedEncoding); assert offered; return msg; }
/** * Parses the response for a token request. * * <p>Throws a {@link TokenError} when the response indicates an error, or when the response could * not be properly parsed. * * <p>Some OAuth implementations may uses a non-standard encoding of the token. */ protected OAuthAccessToken parseTokenResponse(HttpMessage response) { if (response.getStatus() == 200 || response.getStatus() == 400) { String type = response.getHeader("Content-Type"); if (type != null) { if (type.startsWith("text/plain; charset=UTF-8")) { return this.parseUrlEncodedToken(response); } else { if (type.startsWith("application/json")) { return this.parseJsonToken(response); } else { throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService.badresponse")); } } } else { throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService.badresponse")); } } else { throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService.badresponse")); } }
void requestToken(String authorizationCode) { try { String url = this.service_.getTokenEndpoint(); StringBuilder ss = new StringBuilder(); ss.append("grant_type=authorization_code") .append("&client_id=") .append(Utils.urlEncode(this.service_.getClientId())) .append("&client_secret=") .append(Utils.urlEncode(this.service_.getClientSecret())) .append("&redirect_uri=") .append(Utils.urlEncode(this.service_.getGenerateRedirectEndpoint())) .append("&code=") .append(authorizationCode); HttpClient client = new HttpClient(this); client.setTimeout(15); client .done() .addListener( this, new Signal2.Listener<Exception, HttpMessage>() { public void trigger(Exception event1, HttpMessage event2) { OAuthProcess.this.handleToken(event1, event2); } }); Method m = this.service_.getTokenRequestMethod(); if (m == Method.Get) { boolean hasQuery = url.indexOf('?') != -1; url += (hasQuery ? '&' : '?') + ss.toString(); client.get(url); } else { HttpMessage post = new HttpMessage(); post.setHeader("Content-Type", "application/x-www-form-urlencoded"); post.addBodyText(ss.toString()); client.post(url, post); } } catch (Exception e) { e.printStackTrace(); } }
private OAuthAccessToken parseJsonToken(HttpMessage response) { com.google.gson.JsonObject root = new com.google.gson.JsonObject(); com.google.gson.JsonParseException pe = null; try { root = (com.google.gson.JsonObject) new com.google.gson.JsonParser().parse(response.getBody()); } catch (com.google.gson.JsonParseException error) { pe = error; } boolean ok = root != null; if (!ok) { logger.error( new StringWriter().append("parseJsonToken(): ").append(pe.toString()).toString()); throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService.badjson")); } else { if (response.getStatus() == 200) { try { String accessToken = root.get("access_token").getAsString(); int secs = JsonUtils.orIfNullInt(root.get("expires_in"), -1); WDate expires = null; if (secs > 0) { expires = new WDate(new Date()).addSeconds(secs); } String refreshToken = JsonUtils.orIfNullString(root.get("refreshToken"), ""); return new OAuthAccessToken(accessToken, expires, refreshToken); } catch (RuntimeException e) { logger.error( new StringWriter().append("token response error: ").append(e.toString()).toString()); throw new OAuthProcess.TokenError(WString.tr("Wt.Auth.OAuthService.badresponse")); } } else { throw new OAuthProcess.TokenError( WString.tr( "Wt.Auth.OAuthService." + JsonUtils.orIfNullString(root.get("error"), "missing error"))); } } }
/** * Returns {@code true} if and only if the specified message contains the {@code "Expect: * 100-continue"} header. */ public static boolean is100ContinueExpected(HttpMessage message) { // Expect: 100-continue is for requests only. if (!(message instanceof HttpRequest)) { return false; } // It works only on HTTP/1.1 or later. if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) { return false; } // In most cases, there will be one or zero 'Expect' header. String value = message.headers().get(Names.EXPECT); if (value == null) { return false; } if (equalsIgnoreCase(Values.CONTINUE, value)) { return true; } // Multiple 'Expect' headers. Search through them. return message.headers().contains(Names.EXPECT, Values.CONTINUE, true); }
@Override public HttpMessage sendMessageToEndPoint(final URL url) { Assert.notNull(this.httpClient); HttpEntity entity = null; try (CloseableHttpResponse response = this.httpClient.execute(new HttpGet(url.toURI()))) { final int responseCode = response.getStatusLine().getStatusCode(); for (final int acceptableCode : this.acceptableCodes) { if (responseCode == acceptableCode) { LOGGER.debug("Response code received from server matched {}.", responseCode); entity = response.getEntity(); final HttpMessage msg = new HttpMessage(url, IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8)); msg.setContentType(entity.getContentType().getValue()); msg.setResponseCode(responseCode); return msg; } } LOGGER.debug( "Response code did not match any of the acceptable response codes. Code returned {}", responseCode); if (responseCode == HttpStatus.SC_INTERNAL_SERVER_ERROR) { final String value = response.getStatusLine().getReasonPhrase(); LOGGER.error( "There was an error contacting the endpoint: {}; The error:\n{}", url.toExternalForm(), value); } } catch (final Exception e) { LOGGER.error(e.getMessage(), e); } finally { EntityUtils.consumeQuietly(entity); } return null; }
/** * Returns the length of the content. Please note that this value is not retrieved from {@link * HttpContent#content()} but from the {@code "Content-Length"} header, and thus they are * independent from each other. * * @return the content length or {@code defaultValue} if this message does not have the {@code * "Content-Length"} header or its value is not a number */ public static long getContentLength(HttpMessage message, long defaultValue) { String contentLength = message.headers().get(Names.CONTENT_LENGTH); if (contentLength != null) { try { return Long.parseLong(contentLength); } catch (NumberFormatException e) { return defaultValue; } } // We know the content length if it's a Web Socket message even if // Content-Length header is missing. long webSocketContentLength = getWebSocketContentLength(message); if (webSocketContentLength >= 0) { return webSocketContentLength; } // Otherwise we don't. return defaultValue; }
/** * Returns the content length of the specified web socket message. If the specified message is not * a web socket message, {@code -1} is returned. */ private static int getWebSocketContentLength(HttpMessage message) { // WebSockset messages have constant content-lengths. HttpHeaders h = message.headers(); if (message instanceof HttpRequest) { HttpRequest req = (HttpRequest) message; if (HttpMethod.GET.equals(req.getMethod()) && h.contains(Names.SEC_WEBSOCKET_KEY1) && h.contains(Names.SEC_WEBSOCKET_KEY2)) { return 8; } } else if (message instanceof HttpResponse) { HttpResponse res = (HttpResponse) message; if (res.getStatus().code() == 101 && h.contains(Names.SEC_WEBSOCKET_ORIGIN) && h.contains(Names.SEC_WEBSOCKET_LOCATION)) { return 16; } } // Not a web socket message return -1; }
@Override protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf<Object> out) throws Exception { FullHttpMessage currentMessage = this.currentMessage; if (msg instanceof HttpMessage) { assert currentMessage == null; HttpMessage m = (HttpMessage) msg; // Handle the 'Expect: 100-continue' header if necessary. // TODO: Respond with 413 Request Entity Too Large // and discard the traffic or close the connection. // No need to notify the upstream handlers - just log. // If decoding a response, just throw an exception. if (is100ContinueExpected(m)) { ctx.write(CONTINUE.duplicate()); } if (!m.getDecoderResult().isSuccess()) { removeTransferEncodingChunked(m); this.currentMessage = null; out.add(BufUtil.retain(m)); return; } if (msg instanceof HttpRequest) { HttpRequest header = (HttpRequest) msg; this.currentMessage = currentMessage = new DefaultFullHttpRequest( header.getProtocolVersion(), header.getMethod(), header.getUri(), Unpooled.compositeBuffer(maxCumulationBufferComponents)); } else if (msg instanceof HttpResponse) { HttpResponse header = (HttpResponse) msg; this.currentMessage = currentMessage = new DefaultFullHttpResponse( header.getProtocolVersion(), header.getStatus(), Unpooled.compositeBuffer(maxCumulationBufferComponents)); } else { throw new Error(); } currentMessage.headers().set(m.headers()); // A streamed message - initialize the cumulative buffer, and wait for incoming chunks. removeTransferEncodingChunked(currentMessage); } else if (msg instanceof HttpContent) { assert currentMessage != null; // Merge the received chunk into the content of the current message. HttpContent chunk = (HttpContent) msg; CompositeByteBuf content = (CompositeByteBuf) currentMessage.content(); if (content.readableBytes() > maxContentLength - chunk.content().readableBytes()) { // TODO: Respond with 413 Request Entity Too Large // and discard the traffic or close the connection. // No need to notify the upstream handlers - just log. // If decoding a response, just throw an exception. throw new TooLongFrameException( "HTTP content length exceeded " + maxContentLength + " bytes."); } // Append the content of the chunk if (chunk.content().isReadable()) { chunk.retain(); content.addComponent(chunk.content()); content.writerIndex(content.writerIndex() + chunk.content().readableBytes()); } final boolean last; if (!chunk.getDecoderResult().isSuccess()) { currentMessage.setDecoderResult(DecoderResult.failure(chunk.getDecoderResult().cause())); last = true; } else { last = chunk instanceof LastHttpContent; } if (last) { this.currentMessage = null; // Merge trailing headers into the message. if (chunk instanceof LastHttpContent) { LastHttpContent trailer = (LastHttpContent) chunk; currentMessage.headers().add(trailer.trailingHeaders()); } // Set the 'Content-Length' header. currentMessage .headers() .set(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(content.readableBytes())); // All done out.add(currentMessage); } } else { throw new Error(); } }
public static boolean isContentLengthSet(HttpMessage m) { return m.headers().contains(Names.CONTENT_LENGTH); }
/** * Sets a new integer header with the specified name and values. If there is an existing header * with the same name, the existing header is removed. */ public static void setIntHeader( HttpMessage message, CharSequence name, Iterable<Integer> values) { message.headers().set(name, values); }
/** * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked * * @param message The message to check * @return True if transfer encoding is chunked, otherwise false */ public static boolean isTransferEncodingChunked(HttpMessage message) { return message.headers().contains(Names.TRANSFER_ENCODING, Values.CHUNKED, true); }
/** Adds a new integer header with the specified name and value. */ public static void addIntHeader(HttpMessage message, CharSequence name, int value) { message.headers().add(name, value); }
/** * Sets a new date header with the specified name and values. If there is an existing header with * the same name, the existing header is removed. The specified values are formatted as defined in * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a> */ public static void setDateHeader(HttpMessage message, CharSequence name, Iterable<Date> values) { message.headers().set(name, values); }
/** * Adds a new date header with the specified name and value. The specified value is formatted as * defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a> */ public static void addDateHeader(HttpMessage message, CharSequence name, Date value) { message.headers().add(name, value); }
/** Sets the {@code "Host"} header. */ public static void setHost(HttpMessage message, CharSequence value) { message.headers().set(Names.HOST, value); }
/** Returns the value of the {@code "Host"} header. */ public static String getHost(HttpMessage message) { return message.headers().get(Names.HOST); }
/** Sets the {@code "Content-Length"} header. */ public static void setContentLength(HttpMessage message, long length) { message.headers().set(Names.CONTENT_LENGTH, length); }