HTTPReq{{ /** Default Socket connection timeout (20 seconds) */ public static readonly int DEFAULT_CONN_TIMEOUT = 20*1000; /** Default Socket Response timeout (1 second) */ public static readonly int DEFAULT_RESP_TIMEOUT = 1000; /** Create an HTTP Request with a given Header, MessageHeaders, and Body */ public static HTTPReq create(HTTPHead req, List hds, String body){ return createNoLen(req, hds.append(new MsgHead("Content-Length",""+body.Length)), body); } /** Create an HTTP Request with a given Header, MessageHeaders, and Body */ private static HTTPReq createNoLen(HTTPHead req, List hds, String body){ return new HTTPReq(req, hds, new ident(body)); } /** Create an HTTP Get Request for the given (relative) URL. Then use * {@link #send(String,int) Send} for a Request/Response pair. */ public static HTTPReq Get(String url) { return create(HTTPHead.Get(URL.Parse(url)), List.create(), ""); } /** Create an HTTP Get Request for the given relative URL, to be sent to the * given Host. Use this method for raw Socket/Connection sends, but use * {@link #send(String,int) Send} for a Request/Response pair. */ public static HTTPReq Get(String url, String host) { return create(HTTPHead.Get(URL.Parse(url)), hostHeader(host), ""); } /** Create an HTTP Head Request for the given (relative) URL. Then use * {@link #send(String,int) Send} for a Request/Response pair. */ public static HTTPReq Head(String url) { return create(HTTPHead.Get(URL.Parse(url)), List.create(), ""); } /** Create an HTTP Head Request for the given relative URL, to be sent to the * given Host. Use this method for raw Socket/Connection sends, but use * {@link #send(String,int) Send} for a Request/Response pair. */ public static HTTPReq Head(String url, String host) { return create(HTTPHead.Head(URL.Parse(url)), hostHeader(host), ""); } /** Create an HTTP Post Request to the given (relative) URL. Then use * {@link #send(String,int) Send} for a Request/Response pair. */ public static HTTPReq Post(String url, String body) { return create(HTTPHead.Post(URL.Parse(url)), List.create(), body); } /** Create an HTTP Post Request to the given relative URL, to be sent to the * given Host. Use this method for raw Socket/Connection sends, but use * {@link #send(String,int) Send} for a Request Response Pair. */ public static HTTPReq Post(String url, String host, String body) { return create(HTTPHead.Post(URL.Parse(url)), hostHeader(host), body); } private static List hostHeader(String h) { return List.create(new MsgHead(new ident("Host"), new ident(h))); } /** Add the given Message Headers to this Request */ private HTTPReq addHeaders(List hs){ return createNoLen(head,hs.append(keys),""+body); } /** Add a given Message Headers to this Request */ public HTTPReq addHeader(String key, String val){ return createNoLen(head,keys.append(new MsgHead(key,val)),""+body); } /** Request Types */ public enum ReqType{ GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, OPTIONS, OTHER }; /** Return this Request's type */ public ReqType getType(){ return head.getType(); } /** Return the URL arguments (key/value) for this request. The URL arguments * follow the base URL after a '?'. * E.g.: /foo/bar?bazzle=13&wizwoz='hello' */ public Map urlArgs(){ return head.GetUrl().urlArgs(); } /** Return the Body arguments (key/value) from this request. The Body arguments * are on a single line of the body, like URLArgs, but without the '?' */ public Map bodyArgs(){ return splitArgs(""+body); } /** Return the relative URL, without any URL arguments */ public String trimmedUrl(){ return head.GetUrl().trimArgs(); } /** Send this Request to the given Server/Port, and return its Response */ public HTTPResp send(String server, int port){ return send(server,port,0); } /** Send this Request to the given Server/Port, and return its Response with * the given response timeout. If the server does not respond in the given * time an */ public HTTPResp send(String server, int port, int respTimeout){ return send(server,port,HTTPReq.DEFAULT_CONN_TIMEOUT, respTimeout); } /** Send this Request to the given Server/Port, and return its * Response, but only wait for the specified timout (in Milliseconds) */ public HTTPResp send(String server, int port, int connTimeout, int respTimeout) { Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Connect sock.Connect(server, port); // Add the header and Send addHeaders(hostHeader(server+":"+port)).toSocket(sock); // Read the Response HTTPResp res = HTTPResp.fromSocket(sock); sock.Close(); return res; } /** Write this Request to the given Socket */ public void toSocket(Socket s){ try{ StreamWriter outt = new StreamWriter(new NetworkStream(s)); outt.Write(this.ToString()); outt.Flush(); s.Shutdown(SocketShutdown.Send); }catch(Exception e){ throw e; } } /** Read a request from a Socket, timeout if needed */ public static HTTPReq fromSocket(Socket s){ return fromSocket(s,0); } /** Read a request from a Socket */ public static HTTPReq fromSocket(Socket s, long timeout){ try{ // Recieve Timer //s.ReceiveTimeout = HTTPReq.DEFAULT_RESP_TIMEOUT; s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, HTTPReq.DEFAULT_RESP_TIMEOUT); HTTPReq req = fromInputStream(new NetworkStream(s),timeout); s.Shutdown(SocketShutdown.Receive); return req; }catch(Exception e){ throw e; } } /** Read a Request from an InputStream */ private static HTTPReq fromInputStream(Stream inpt){ return fromInputStream(inpt,0); } /** Read a Request from an InputStream */ public static HTTPReq fromInputStream(Stream inpt, long timeout){ try{ StreamReader inn = new StreamReader(inpt); String first = readLine(inn,timeout); if(first == null)throw new Exception("Empty HTTP Header"); HTTPHead h = HTTPHead.Parse(first); return new HTTPReq(h, ParseMsgHeads(inn,timeout), ParseBody(inn,timeout)); }catch(Exception e){ throw e; } } /** Parse a List of Message Headers */ public static List ParseMsgHeads(StreamReader inn, long timeout){ String line = null; if((line = inn.ReadLine()) == null || line.Length == 0) return List.create(); int colon = line.IndexOf(":"); return ParseMsgHeads(inn,timeout) .push(new MsgHead(new ident(line.Substring(0,colon)), new ident(line.Substring(colon+2)))); } /** Read a single Line */ public static String readLine(StreamReader inn, long timeout){ long start = millis(); String line = ""; while(true){ line = null; try{ line = inn.ReadLine(); return line; }catch(IOException e){ if(timeout > 0 && (millis()-start) > timeout) throw e; } } } static long millis(){ return System.DateTime.Now.Ticks/10000; } /** Parse the Body of a Request */ public static ident ParseBody(StreamReader inn, long timeout){ long start = millis(); StringBuilder sb = new StringBuilder(); char[] buff = new char[1024]; int many = 0; if(!inn.EndOfStream) do{ many = 0; try{ many = inn.Read(buff,0,1024); if(many>0) sb.Append(buff,0,many); }catch(IOException e){ if(timeout > 0 && (millis()-start) > timeout) throw e; } }while(!inn.EndOfStream || many > 0); return new ident(sb.ToString()); } /** Return the Body of this Request */ public String getBodyString(){ return ""+body; } /** Return the Headers of this Request as a Map */ public Map getHeaders(){ return getHeaders(keys); } class HeadFold : List.Fold>{ public override Map fold(MsgHead h, Map m){ return m.put(""+h.GetKey(),""+h.GetValue()); } } /** Return the Headers of this Request as a Map */ public static Map getHeaders(List hds){ return hds.fold(new HeadFold(), Map.create()); } class SplitFold : List.Fold>{ public override Map fold(String p, Map m){ String[] kv = p.Split('='); if(kv.Length < 2)return m; return m.put(kv[0],kv[1]); } } /** Split an argument String into key/value Map */ public static Map splitArgs(String s){ return List.create(s.Split('&')).fold(new SplitFold(), Map.create()); } /** Decode an encoded URL */ public static String decodeURL(String url){ //return System.Web.HttpUtility.UrlDecode(url); return Uri.UnescapeDataString(url); } /** Encode a (possibly special) URL */ public static String encodeURL(String url){ //return System.Web.HttpUtility.UrlEncode(url); return Uri.EscapeDataString(url); } }} HTTPResp{{ /** Basic Response Numbers */ public static readonly int OK = 200, NOT_FOUND = 404, MIN_OK = OK, MAX_OK = 299, MIN_ERROR = 400, MIN_SERVER_ERROR = 500, MAX_ERROR = 599; /** Typical HTTP Version */ public static readonly HTTPVer VER = new HTTPVer(1.0); /** Create a response with the given number, description, headers, and body */ public static HTTPResp create(int resp, String desc, List hds, String body){ return createNoLen(resp, new ident(desc), hds.append(new MsgHead("Content-Length",""+body.Length)), new ident(body)); } /** Create a response without adding the Length */ private static HTTPResp createNoLen(int resp, ident label, List hds, ident body){ return new HTTPResp(VER, resp, label, hds, body); } /** Create an OK Response with the given Content-Type/Body */ public static HTTPResp ok(String type, String body){ return create(OK, "OK", commonHeaders(type), body); } /** Common Headers: Date, Content-Type */ private static List commonHeaders(String type){ return List.create( new MsgHead("Date",""+DateTime.Now), new MsgHead("Content-Type",type)); } /** Create an (empty) Error Response */ public static HTTPResp error(){ return textError(""); } /** Create an Error Response with t*/ public static HTTPResp error(String body){ return textError(body); } /** Create an (empty) Error Response */ public static HTTPResp error(int errCode, String msg, String body){ return textError(errCode,msg,body); } /** Create an TEXT Error (File Not Found" Response with a Body */ public static HTTPResp textError(String body){ return htmlError(NOT_FOUND, "Not Found", body); } /** Create an Texr Error Response with the given Code, Message and Body */ public static HTTPResp textError(int errCode, String msg, String body){ return create(errCode, msg, commonHeaders("text/plain"), body); } /** Create an HTML Error "File Not Found") Response with a Body */ public static HTTPResp htmlError(String body){ return htmlError(NOT_FOUND, "Not Found", body); } /** Create an HTML Error Response with a Body */ public static HTTPResp htmlError(int errCode, String msg, String body){ return create(errCode, msg, commonHeaders("text/html"), body); } /** Create a Plain Text Response */ public static HTTPResp textResponse(String text){ return ok("text/plain", text); } /** Create an HTML Response */ public static HTTPResp htmlResponse(String text){ return ok("text/html", text); } /** Add a given Message Header to this Response */ public HTTPResp addHeader(String key, String val){ return createNoLen(resp,label,keys.append(new MsgHead(key,val)),body); } /** Is this Response OK? */ public bool isOK(){ return resp >= MIN_OK && resp <= MAX_OK; } /** Is this Response an Error? */ public bool isError(){ return resp >= MIN_ERROR && resp <= MAX_ERROR; } /** Is this Response a Server Error? */ public bool isServerError(){ return resp >= MIN_SERVER_ERROR && resp <= MAX_ERROR; } /** Is this Response a Client Error? */ public bool isClientError(){ return resp >= MIN_ERROR && resp < MIN_SERVER_ERROR; } /** Write a Response to a Socket */ public void toSocket(Socket s){ try{ StreamWriter outt = new StreamWriter(new NetworkStream(s)); outt.Write(this.ToString()); outt.Flush(); s.Shutdown(SocketShutdown.Send); }catch(Exception e){ throw e; } } /** Read a Response from a Socket */ public static HTTPResp fromSocket(Socket s){ return fromSocket(s,0); } /** Read a Response from a Socket */ public static HTTPResp fromSocket(Socket s, int respTimeout){ try{ //s.ReceiveTimeout = HTTPReq.DEFAULT_RESP_TIMEOUT; s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, HTTPReq.DEFAULT_RESP_TIMEOUT); HTTPResp resp = fromInputStream(new NetworkStream(s), respTimeout); s.Shutdown(SocketShutdown.Receive); return resp; }catch(Exception e){ throw e; } } /** Read a Response from an Input Stream */ public static HTTPResp fromInputStream(Stream inpt, long timeout){ try{ StreamReader inn = new StreamReader(inpt); String first = HTTPReq.readLine(inn,timeout); if(first == null)throw new Exception("Empty HTTP Header"); HTTPVer v = HTTPVer.Parse(first); first = first.Substring(first.IndexOf(' ')+1); int b = first.IndexOf(' '), rnum = Int32.Parse(first.Substring(0,b)); return new HTTPResp(v, rnum, new ident(first.Substring(b+1)), HTTPReq.ParseMsgHeads(inn,timeout), HTTPReq.ParseBody(inn,timeout)); }catch(Exception e){ throw e; } } /** Get the body of this Response */ public String getBodyString(){ return ""+body; } /** Return the headers (key/value Map) of this Response */ public Map getHeaders(){ return HTTPReq.getHeaders(keys); } }} HTTPHead{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.OTHER; } /** Return a Get Request for the given URL */ public static HTTPHead Get(URL url) { return new GetReq(url, HTTPResp.VER); } /** Return a Head Request for the given URL */ public static HTTPHead Head(URL url) { return new HeadReq(url, HTTPResp.VER); } /** Return a Post Request for the given URL */ public static HTTPHead Post(URL url) { return new PostReq(url, HTTPResp.VER); } }} GetReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.GET; } }} PostReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.POST; } }} HeadReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.HEAD; } }} PutReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.PUT; } }} DeleteReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.DELETE; } }} TraceReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.TRACE; } }} ConnectReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.CONNECT; } }} OptionsReq{{ /** Get the ReqType of this Request */ public HTTPReq.ReqType getType(){ return HTTPReq.ReqType.OPTIONS; } }} MsgHead{{ /** Create a MsgHeader from key/value Strings */ public MsgHead(String k, String v):this(new ident(k), new ident(v)){} }} URL{{ /** Is this URL Empty? */ public bool isEmpty(){ return false; } /** Return the URLs Arguments */ public abstract Map urlArgs(); /** Remove the URLs Arguments */ public abstract String trimArgs(); }} NoURL{{ /** Is this URL Empty? */ public bool isEmpty(){ return true; } /** Return the URLs Arguments */ public override Map urlArgs(){ return Map.create(); } /** Remove the URLs Arguments */ public override String trimArgs(){ return ""; } }} BaseURL{{ /** Return the URLs Arguments */ public override Map urlArgs(){ return rest.urlArgs(); } /** Remove the URL Arguments */ public override String trimArgs(){ return "/"+rest.trimArgs(); } }} MidURL{{ /** Return the URLs Arguments */ public override Map urlArgs(){ Map r = rest.urlArgs(); if(!rest.isEmpty())return r; String idS = id.ToString(); int amp = idS.IndexOf('?'); if(amp<0)return r; idS = idS.Substring(idS.IndexOf('?')+1).Trim(); return HTTPReq.splitArgs(idS); } /** Remove the URLs Arguments */ public override String trimArgs(){ if(!rest.isEmpty())return id+rest.trimArgs(); String idS = id.ToString(); int amp = idS.IndexOf('?'); return amp<0 ? idS: idS.Substring(0,amp); } }}