package erjang.driver.tcp_inet;
import kilim.Pausable;
import erjang.EAtom;
import erjang.EBinary;
import erjang.EObject;
import erjang.ERT;
import erjang.EString;
import erjang.ETuple;
import erjang.ETuple2;
import erjang.driver.tcp_inet.Packet.HTTPAtom;
import erjang.driver.tcp_inet.TCPINet.ActiveType;
import erjang.driver.tcp_inet.TCPINet.AsyncOp;
public class TCPINetCallbacks extends PacketCallbacks<TCPINet> {
private static final EAtom am_http_request = EAtom.intern("http_request");
private static final EAtom am_inet_async = EAtom.intern("inet_async");
private static final EAtom am_http = EAtom.intern("http");
private static final EAtom am_star = EAtom.intern("*");
private static final EAtom am_abs_path = EAtom.intern("abs_path");
private static final EAtom am_https = EAtom.intern("https");
private static final EAtom am_absoluteURI = EAtom.intern("absoluteURI");
private static final EAtom am_scheme = EAtom.intern("scheme");
private static final EAtom am_http_header = EAtom.intern("http_header");
private static final EAtom am_http_eoh = EAtom.intern("http_eoh");
private static final EAtom am_http_error = EAtom.intern("http_error");
@Override
int http_eoh(TCPINet desc) throws Pausable {
return send_http(desc, am_http_eoh);
}
@Override
int http_error(TCPINet desc, byte[] data, int pos, int len) throws Pausable {
EObject line = load_string(desc, data, pos, len);
ETuple2 req = new ETuple2(am_http_error, line);
if (desc.active == ActiveType.PASSIVE) {
/* {inet_async, S, Ref, {error,{http_error,Line}}} */
AsyncOp op = desc.deq_async();
if (op == null) {
return -1;
}
ETuple2 ok = new ETuple2(ERT.am_error, req);
ETuple msg = ETuple.make(am_inet_async, desc.port(), ERT.box(op.id), ok);
desc.driver_send_term(op.caller, msg);
} else {
/* {http, S, {http_error, Line}}} */
ETuple http = ETuple.make(am_http, desc.port(), req);
desc.driver_output_term(http);
}
return 0;
}
@Override
int http_header(TCPINet desc, HTTPAtom name, byte[] nameBuf, int namePtr,
int nameLen, byte[] valBuf, int valPtr, int valLen) throws Pausable {
EObject bit = (name == null)
? ERT.box(0)
: ERT.box( name.index + 1 );
EObject nam = (name == null)
? load_string(desc, nameBuf, namePtr, nameLen)
: name.atom;
EObject value = load_string(desc, valBuf, valPtr, valLen);
ETuple req = ETuple.make(am_http_header, bit, nam, ERT.am_undefined, value);
return send_http(desc, req);
}
@Override
int http_request(TCPINet desc, HTTPAtom method, byte[] data, int meth_ptr,
int meth_len, PacketHttpURI uri, int major, int minor) throws Pausable {
EObject meth =
(method == null)
? load_string(desc, data, meth_ptr, meth_len)
: method.atom;
ETuple version = new ETuple2(ERT.box(major), ERT.box(minor));
ETuple req = ETuple.make(am_http_request, meth, load_uri(desc, uri), version);
return send_http(desc, req);
}
private int send_http(TCPINet desc, EObject req) throws Pausable {
if (desc.active == ActiveType.PASSIVE) {
/* {inet_async, S, Ref, {ok,{http_request,Meth,Uri,Version}}} */
AsyncOp op = desc.deq_async();
if (op == null) {
return -1;
}
ETuple2 ok = new ETuple2(ERT.am_ok, req);
ETuple msg = ETuple.make(am_inet_async, desc.port(), ERT.box(op.id), ok);
desc.driver_send_term(op.caller, msg);
} else {
/* {http, S, {http_request,Meth,Uri,Version}}} */
ETuple http = ETuple.make(am_http, desc.port(), req);
desc.driver_output_term(http);
}
return 0;
}
// HttpUri = '*'
// | {absoluteURI, http|https, Host=HttpString, Port=int()|undefined, Path=HttpString}
// | {scheme, Scheme=HttpString, HttpString}
// | {abs_path, HttpString}
// | HttpString
private EObject load_uri(TCPINet desc, PacketHttpURI uri) {
EAtom scheme = null;
switch (uri.type) {
case URI_STAR:
return (EAtom)am_star;
case URI_ABS_PATH:
return new ETuple2(am_abs_path, load_string(desc, uri.s1_data, uri.s1_ptr, uri.s1_len));
case URI_HTTPS:
scheme = am_https;
case URI_HTTP:
if (scheme == null) { scheme = am_http; }
return ETuple.make(am_absoluteURI,
scheme,
load_string(desc, uri.s1_data, uri.s1_ptr, uri.s1_len),
(uri.port==0 ? ERT.am_undefined : ERT.box(uri.port)),
load_string(desc, uri.s2_data, uri.s2_ptr, uri.s2_len));
case URI_STRING:
return load_string(desc, uri.s1_data, uri.s1_ptr, uri.s1_len);
case URI_SCHEME:
return ETuple.make(am_scheme,
load_string(desc, uri.s1_data, uri.s1_ptr, uri.s1_len),
load_string(desc, uri.s2_data, uri.s2_ptr, uri.s2_len));
}
throw new InternalError("should not happen");
}
private EObject load_string(TCPINet desc, byte[] data, int data_off,
int data_len) {
switch(desc.htype) {
case TCP_PB_HTTPH_BIN:
case TCP_PB_HTTP_BIN:
return EBinary.make(data, data_off, data_len, 0 /*extra bits*/);
default:
return EString.make(data, data_off, data_len);
}
}
@Override
int http_response(TCPINet arg, int major, int minor, int status,
byte[] data, int off, int len) {
throw new erjang.NotImplemented();
}
}