All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dsa.iot.netty.CustomHttpProvider.scala Maven / Gradle / Ivy

There is a newer version: 0.4.0
Show newest version
package org.dsa.iot.netty

import java.util.Map

import collection.JavaConverters.mapAsScalaMapConverter
import util.control.NonFatal

import org.dsa.iot.dslink.provider.HttpProvider
import org.dsa.iot.dslink.util.URLInfo
import org.dsa.iot.dslink.util.http.HttpResp
import org.dsa.iot.shared.SharedObjects

import io.netty.bootstrap.Bootstrap
import io.netty.channel._
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.http._
import io.netty.handler.ssl.SslContextBuilder
import io.netty.handler.ssl.util.InsecureTrustManagerFactory
import io.netty.util.CharsetUtil

/**
 * HTTP Provider implementation.
 */
class CustomHttpProvider extends HttpProvider {

  def post(url: URLInfo, content: Array[Byte], headers: Map[String, String]): HttpResp = {
    if (url == null)
      throw new NullPointerException("url")

    try {
      val handler = new HttpHandler

      val b = new Bootstrap
      b.group(SharedObjects.getLoop)
      b.channel(classOf[NioSocketChannel])
      b.handler(new Initializer(handler, url.secure))

      val fut = b.connect(url.host, url.port)
      val chan = fut.sync.channel
      chan.writeAndFlush(populateRequest(url.path, content, headers))
      fut.channel.closeFuture.sync
      populateResponse(handler)
    } catch {
      case NonFatal(e) => throw new RuntimeException(e)
    }
  }

  private def populateRequest(uri: String, content: Array[Byte], headers: Map[String, String]): HttpRequest = {
    val request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri)
    val buf = request.content
    if (content != null)
      buf.writeBytes(content)

    {
      val h = request.headers
      Option(headers) foreach (_.asScala foreach {
        case (name, value) => h.set(name, value)
      })
      val len = String.valueOf(buf.readableBytes)
      h.set(HttpHeaders.Names.CONTENT_LENGTH, len)
    }
    request
  }

  private def populateResponse(handler: HttpHandler): HttpResp = {
    val throwable = handler.getThrowable
    if (throwable != null)
      throw throwable

    val resp = new HttpResp()
    resp.setStatus(handler.getStatus)
    resp.setBody(handler.getContent)
    resp
  }
}

/**
 * Initializes the HTTP channel.
 */
private[netty] class Initializer(handler: HttpHandler, secure: Boolean)
    extends ChannelInitializer[SocketChannel] {

  override protected def initChannel(ch: SocketChannel) = {
    val p = ch.pipeline

    if (secure) {
      val man = InsecureTrustManagerFactory.INSTANCE
      val con = SslContextBuilder.forClient.trustManager(man).build
      p.addLast(con.newHandler(ch.alloc()))
    }

    p.addLast(new HttpClientCodec)
    p.addLast(new HttpContentDecompressor)
    p.addLast(handler)
  }
}

/**
 * Reads HTTP data.
 */
private[netty] class HttpHandler extends SimpleChannelInboundHandler[Object] {
  private val content = new StringBuffer
  private var status: HttpResponseStatus = null
  private var t: Throwable = null

  protected def channelRead0(ctx: ChannelHandlerContext, msg: Object) = {
    if (msg.isInstanceOf[HttpResponse])
      status = msg.asInstanceOf[HttpResponse].getStatus
    if (msg.isInstanceOf[HttpContent]) {
      val buf = msg.asInstanceOf[HttpContent].content
      content.append(buf.toString(CharsetUtil.UTF_8))
    }
    if (msg.isInstanceOf[LastHttpContent])
      ctx.close
  }

  override def exceptionCaught(ctx: ChannelHandlerContext, t: Throwable) = {
    this.t = t
    ctx.close
  }

  def getThrowable = t

  def getStatus = status

  def getContent = content.toString
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy