2121import Foundation
2222import NanoHTTP
2323import DynamicJSON
24+ #if os(iOS) || os(watchOS) || os(tvOS)
25+ import UIKit
26+ #elseif os(macOS)
27+ import Cocoa
28+ #endif
2429
2530public final class HTTPServerLibrary : NativeLibrary {
2631
@@ -69,6 +74,7 @@ public final class HTTPServerLibrary: NativeLibrary {
6974 self . define ( Procedure ( " make-http-server " , self . makeHttpServer) )
7075 self . define ( Procedure ( " http-server-running? " , self . isHttpServerRunning) )
7176 self . define ( Procedure ( " http-server-port " , self . httpServerPort) )
77+ self . define ( Procedure ( " http-server-ipv4 " , self . isHttpServerIPv4) )
7278 self . define ( Procedure ( " http-server-open-connections " , self . httpServerOpenConnections) )
7379 self . define ( Procedure ( " http-server-routes " , self . httpServerRoutes) )
7480 self . define ( Procedure ( " http-server-handlers " , self . httpServerHandlers) )
@@ -107,12 +113,12 @@ public final class HTTPServerLibrary: NativeLibrary {
107113 self . define ( Procedure ( " srv-request-form-attributes " , self . serverRequestFormAttributes) )
108114 self . define ( Procedure ( " srv-request-form-multiparts " , self . serverRequestFormMultiparts) )
109115 self . define ( Procedure ( " srv-request-address " , self . serverRequestAddress) )
110- self . define ( Procedure ( " srv-request-address " , self . serverRequestAddress) )
111116 self . define ( Procedure ( " _srv-request-send-response " , self . serverRequestSendResponse) , export: false )
112117 self . define ( Procedure ( " srv-multipart? " , self . isServerMultipart) )
113118 self . define ( Procedure ( " srv-multipart-headers " , self . serverMultipartHeaders) )
114119 self . define ( Procedure ( " srv-multipart-header " , self . serverMultipartHeader) )
115120 self . define ( Procedure ( " srv-multipart-body " , self . serverMultipartBody) )
121+ self . define ( Procedure ( " srv-multipart-body->string " , self . serverMultipartBodyToString) )
116122 self . define ( Procedure ( " srv-response? " , self . isSrvResponse) )
117123 self . define ( Procedure ( " _make-srv-response " , self . makeSrvResponse) , export: false )
118124 self . define ( Procedure ( " srv-response-created " , self . srvResponseCreated) )
@@ -377,7 +383,9 @@ public final class HTTPServerLibrary: NativeLibrary {
377383 private func httpServerRegister( expr: Expr , fst: Expr , snd: Expr , thd: Expr ? ) throws -> Expr {
378384 if let handler = thd {
379385 _ = try handler. asProcedure ( )
380- try self . httpServer ( from: expr) . register ( method: try fst. asString ( ) . uppercased ( ) ,
386+ try self . httpServer ( from: expr) . register ( method: fst. isFalse
387+ ? nil
388+ : try fst. asString ( ) . uppercased ( ) ,
381389 path: try snd. asString ( ) ,
382390 handler: handler)
383391 } else {
@@ -483,17 +491,31 @@ public final class HTTPServerLibrary: NativeLibrary {
483491 return . makeString( try self . httpServerRequest ( from: expr) . connection. request. path)
484492 }
485493
486- private func serverRequestQuery( expr: Expr ) throws -> Expr {
494+ private func serverRequestQuery( expr: Expr , inclPath : Expr ? ) throws -> Expr {
487495 let req = try self . httpServerRequest ( from: expr)
488496 var components = URLComponents ( )
489- components. path = req. connection. request. path
490- if !req. connection. request. queryParams. isEmpty {
497+ if req. connection. request. queryParams. isEmpty {
498+ if inclPath? . isTrue ?? false {
499+ components. path = req. connection. request. path
500+ } else {
501+ return . makeString( " " )
502+ }
503+ } else {
504+ if inclPath? . isTrue ?? false {
505+ components. path = req. connection. request. path
506+ }
491507 components. queryItems = req. connection. request. queryParams. map { k, v in
492508 return URLQueryItem ( name: k. removingPercentEncoding ?? k,
493509 value: v. removingPercentEncoding ?? v)
494510 }
495511 }
496- return . makeString( components. url? . absoluteString ?? req. connection. request. path)
512+ if let str = components. string {
513+ return . makeString( str)
514+ } else if inclPath? . isTrue ?? false {
515+ return . makeString( req. connection. request. path)
516+ } else {
517+ return . makeString( " " )
518+ }
497519 }
498520
499521 private func serverRequestPathParam( expr: Expr , name: Expr , default: Expr ? ) throws -> Expr {
@@ -662,6 +684,14 @@ public final class HTTPServerLibrary: NativeLibrary {
662684 return . bytes( MutableBox ( try self . multiPart ( from: expr) . multipart. body) )
663685 }
664686
687+ private func serverMultipartBodyToString( expr: Expr ) throws -> Expr {
688+ guard let str = String ( bytes: try self . multiPart ( from: expr) . multipart. body,
689+ encoding: . utf8) else {
690+ return . false
691+ }
692+ return . makeString( str)
693+ }
694+
665695 // HTTP server response functionality
666696
667697 private func isSrvResponse( expr: Expr ) -> Expr {
@@ -770,12 +800,72 @@ public final class HTTPServerLibrary: NativeLibrary {
770800 return . void
771801 }
772802
803+ #if os(iOS) || os(watchOS) || os(tvOS)
804+ private func image( _ image: NativeImage , to mimeType: String ) -> Data ? {
805+ var type : BitmapImageFileType
806+ switch mimeType {
807+ case " image/tiff " :
808+ type = . tiff
809+ case " image/png " :
810+ type = . png
811+ case " image/jpeg " :
812+ type = . jpeg
813+ case " image/gif " :
814+ type = . gif
815+ case " image/bmp " :
816+ type = . bmp
817+ default :
818+ return nil
819+ }
820+ return type. data ( for: image. value)
821+ }
822+ #elseif os(macOS)
823+ private func image( _ image: NativeImage , to mimeType: String ) -> Data ? {
824+ var type : NSBitmapImageRep . FileType
825+ switch mimeType {
826+ case " image/tiff " :
827+ type = . tiff
828+ case " image/png " :
829+ type = . png
830+ case " image/jpeg " :
831+ type = . jpeg
832+ case " image/gif " :
833+ type = . gif
834+ case " image/bmp " :
835+ type = . bmp
836+ default :
837+ return nil
838+ }
839+ for repr in image. value. representations {
840+ if let bitmapRepr = repr as? NSBitmapImageRep {
841+ return bitmapRepr. representation ( using: type, properties: [ : ] )
842+ }
843+ }
844+ return nil
845+ }
846+ #endif
847+
773848 private func srvResponseBody( from obj: Expr , contentType: Expr ? ) throws -> NanoHTTPResponse . Body {
774849 switch obj {
775850 case . false :
776851 return . empty
777- case . string( _) :
778- return . text( try obj. asString ( ) )
852+ case . string( let str) :
853+ if let ct = try contentType? . asString ( ) {
854+ switch ct {
855+ case " text/plain " :
856+ return . text( str as String )
857+ case " text/html " :
858+ return . html( str as String )
859+ default :
860+ if let data = ( str as String ) . data ( using: . utf8) {
861+ return . data( data, contentType: ct)
862+ } else {
863+ return . text( str as String )
864+ }
865+ }
866+ } else {
867+ return . text( str as String )
868+ }
779869 case . bytes( let bv) :
780870 return . data( Data ( bv. value) , contentType: try contentType? . asString ( ) )
781871 case . object( let obj) :
@@ -784,15 +874,28 @@ public final class HTTPServerLibrary: NativeLibrary {
784874 } else if let mutable = obj as? MutableJSON {
785875 return . json( mutable. value)
786876 } else if let str = obj as? StyledText {
877+ if contentType? . isTrue ?? false {
878+ let data = try str. value. data (
879+ from: NSMakeRange ( 0 , str. value. length) ,
880+ documentAttributes: [ . documentType : NSAttributedString . DocumentType. rtf] )
881+ return . data( data, contentType: " application/rtf " )
882+ }
787883 let data = try str. value. data (
788- from: NSRange ( location: 0 , length: str. value. length) ,
789- documentAttributes: [ . documentType: NSAttributedString . DocumentType. html,
790- . characterEncoding: String . Encoding. utf8. rawValue] )
884+ from: NSRange ( location: 0 , length: str. value. length) ,
885+ documentAttributes: [ . documentType: NSAttributedString . DocumentType. html,
886+ . characterEncoding: String . Encoding. utf8. rawValue] )
791887 if let res = String ( data: data, encoding: String . Encoding. utf8) {
792888 return . htmlBody( res)
793889 } else {
794890 fallthrough
795891 }
892+ } else if let image = obj as? NativeImage {
893+ let ct = try contentType? . asString ( ) ?? " image/png "
894+ if let data = self . image ( image, to: ct) {
895+ return . data( data, contentType: ct)
896+ } else {
897+ fallthrough
898+ }
796899 } else {
797900 fallthrough
798901 }
@@ -824,7 +927,8 @@ public final class HTTPServerLibrary: NativeLibrary {
824927 let name = try name. asString ( ) . lowercased ( )
825928 var list = expr
826929 while case . pair( let element, let next) = list {
827- if case . pair( . string( let str) , let value) = element, ( str as String ) == name {
930+ if case . pair( . string( let str) , let value) = element,
931+ ( str as String ) . lowercased ( ) == name {
828932 return value
829933 }
830934 list = next
@@ -851,6 +955,8 @@ public final class HTTPServerLibrary: NativeLibrary {
851955 }
852956 current = value. index ( after: current)
853957 start = current
958+ } else {
959+ fallthrough
854960 }
855961 default :
856962 current = value. index ( after: current)
0 commit comments